Codeforces 1312E. Array Shrinking(区间DP 栈)
作者:互联网
题意:
给出一个长度为
n
n
n的序列,对于相同相邻的两个数
x
x
x可以替换为一个数
x
+
1
x+1
x+1,问该序列的最短长度是多少。
n
<
=
500
n<=500
n<=500
思路:
看到数据范围不难想到是个区间dp
d
p
[
l
]
[
r
]
dp[l][r]
dp[l][r]表示区间
[
l
,
r
]
[l,r]
[l,r]能够合并成的最短序列长度。
那么
d
p
[
l
]
[
r
]
=
m
i
n
(
d
p
[
l
]
[
k
]
+
d
p
[
k
+
1
]
[
r
]
)
dp[l][r]=min(dp[l][k]+dp[k+1][r])
dp[l][r]=min(dp[l][k]+dp[k+1][r])
什么时候
[
l
,
k
]
[l,k]
[l,k]和
[
k
+
1
,
r
]
[k+1,r]
[k+1,r]也可以合并呢?当两个区间的最短长度为
1
1
1并且剩下的数相同时。
记
b
[
l
]
[
r
]
b[l][r]
b[l][r]表示区间
[
l
,
r
]
[l,r]
[l,r]合并后剩余的数
就可以转移了。
时间复杂度
O
(
n
3
)
O(n^3)
O(n3)
代码:
// Problem: E. Array Shrinking
// Contest: Codeforces - Educational Codeforces Round 83 (Rated for Div. 2)
// URL: https://codeforces.com/problemset/problem/1312/E
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;
typedef long long ll;
const int maxn=2e5+100;
int a[510],b[510][510],dp[510][510];
int main() {
int n;cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
dp[i][j]=j-i+1;
if(i==j) b[i][i]=a[i];
}
for(int len=1;len<=n;len++){
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
for(int k=l;k<=r-1;k++){
dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]);
if(dp[l][k]==1&&dp[k+1][r]==1&&b[l][k]==b[k+1][r]){
dp[l][r]=1;
b[l][r]=b[l][k]+1;
}
}
}
}
cout<<dp[1][n]<<endl;
return 0;
}
思路:
考虑用另一种表示状态:
d
p
[
i
]
dp[i]
dp[i]表示
[
1
,
i
]
[1,i]
[1,i]合并后的最短长度,那么最后的答案便是
[
1
,
n
]
[1,n]
[1,n]
转移的话,如果说
[
k
,
i
]
[k,i]
[k,i]可以合并为一个数,那么显然
d
p
[
i
]
=
d
p
[
k
−
1
]
+
1
dp[i]=dp[k-1]+1
dp[i]=dp[k−1]+1
问题就转化为了如何快速判断区间
[
k
,
i
]
[k,i]
[k,i]能否合并为一个数。
延伸出两种思路:
- 借助括号匹配的思想,运用栈来判断。具体做法为:枚举右端点,对于每一个右端点,维护一个栈,然后倒着向前枚举,如果当前元素 x x x和栈顶元素相同,弹出栈顶,说明两者合并变为了 x + 1 x+1 x+1。最后如果说栈的大小为 1 1 1说明该段区间可以合并为一个数。
- 用区间 d p dp dp来解, f [ l ] [ r ] f[l][r] f[l][r]表示区间 [ l , r ] [l,r] [l,r]合并后的数为多少,如果为 0 0 0则说明无法合并为一个数。枚举中间点,如果说 f [ l ] [ k ] = = f [ k + 1 ] [ r ] f[l][k]==f[k+1][r] f[l][k]==f[k+1][r] 则 f [ l ] [ r ] = f [ l ] [ k ] + 1 f[l][r]=f[l][k]+1 f[l][r]=f[l][k]+1.
前者的时间复杂度是 O ( n 2 ) O(n^2) O(n2),后者为 O ( n 3 ) O(n^3) O(n3)
代码:
栈
// Problem: E. Array Shrinking
// Contest: Codeforces - Educational Codeforces Round 83 (Rated for Div. 2)
// URL: https://codeforces.com/problemset/problem/1312/E
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;
typedef long long ll;
const int maxn=2e5+100;
int a[510],b[510][510],dp[510];
int main() {
int n;cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int r=1;r<=n;r++){
dp[r]=dp[r-1]+1;
stack<int>st;
st.push(a[r]);
for(int l=r-1;l;l--){
int now=a[l];
while(1){
if(st.empty()){
st.push(now);break;
}
if(now==st.top()) st.pop(),now++;
else{
st.push(now);break;
}
}
if(st.size()==1) dp[r]=min(dp[r],dp[l-1]+1);
}
}
cout<<dp[n]<<endl;
return 0;
}
区间dp
// Problem: E. Array Shrinking
// Contest: Codeforces - Educational Codeforces Round 83 (Rated for Div. 2)
// URL: https://codeforces.com/problemset/problem/1312/E
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;
typedef long long ll;
const int maxn=2e5+100;
int a[510],f[510][510],dp[510];
int main() {
int n;cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],f[i][i]=a[i];
for(int len=1;len<=n;len++)
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
for(int k=l;k<=r;k++)
if(f[l][k]!=0&&f[l][k]==f[k+1][r])
f[l][r]=f[l][k]+1;
}
memset(dp,0x3f,sizeof dp);
dp[0]=0;
for(int r=1;r<=n;r++)
for(int l=1;l<=r;l++)
if(f[l][r]!=0) dp[r]=min(dp[r],dp[l-1]+1);
cout<<dp[n];
return 0;
}
总结:
d
p
dp
dp状态不同决定了时间复杂度的不同,对于某个
d
p
dp
dp状态来说,又会有好几种解法。
参考:
博客1
博客2
标签:1312E,int,Codeforces,len,510,Array,include,dp 来源: https://blog.csdn.net/weixin_45675097/article/details/120852585