CF 1013E Hills——隔项转移的DP
题目:http://codeforces.com/contest/1013/problem/E
设 dp[ i ][ j ][ 0/1 ] 表示前 i 个位置,有 j 个山峰,第 i 个位置不是/是山峰的最小代价。
dp[ i ][ j ][ 0 ] 可以从 dp[ i-1 ][ j ][ 0/1 ] 转移,从 1 转移的话要调整成 a[ i ] <= a[ i-1 ] ,因为 i-1 是山峰,所以它的高度一定还是原高度 a[ i-1 ],从 0 转移没有要求。
dp[ i ][ j ][ 1 ] 需要从 dp[ i-2 ][ j-1 ][ 0/1 ] 转移,这样才能知道 i-2 位置的高度到底是多少。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; int Mx(int a,int b){return a>b?a:b;} int Mn(int a,int b){return a<b?a:b;} const int N=5005; int n,a[N],dp[N][N][2]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); memset(dp,0x3f,sizeof dp); dp[0][0][0]=dp[1][0][0]=dp[1][1][1]=0; for(int i=2,lm=i+1>>1;i<=n;i++,lm=i+1>>1) for(int j=0;j<=lm;j++) { dp[i][j][0]=dp[i-1][j][0]; int w=0; if(a[i]>=a[i-1])w=a[i]-a[i-1]+1; dp[i][j][0]=Mn(dp[i][j][0],dp[i-1][j][1]+w); if(!j)continue; dp[i][j][1]=dp[i-2][j-1][0]; if(a[i-1]>=a[i])dp[i][j][1]+=a[i-1]-a[i]+1; w=0; if(a[i-1]>=a[i-2])w+=a[i-1]-a[i-2]+1; int tp=Mn(a[i-2]-1,a[i-1]); if(tp>=a[i])w+=tp-a[i]+1; dp[i][j][1]=Mn(dp[i][j][1],dp[i-2][j-1][1]+w); } /* for(int i=1,lm=i+1>>1;i<=n;i++,lm=i+1>>1) for(int j=0;j<=lm;j++) { dp[i][j][0]=dp[i-1][j][0]; h[i][j][0]=a[i]; int w; if(a[i]>=h[i-1][j][1])w=a[i]-h[i-1][j][1]+1; else w=0; if(dp[i-1][j][1]+w<dp[i][j][0]) dp[i][j][0]=dp[i-1][j][1]+w,h[i][j][0]=h[i-1][j][1]-1; if(j) { dp[i][j][1]=dp[i-1][j-1][0]; h[i][j][1]=a[i]; if(h[i-1][j-1][0]>=a[i]) dp[i][j][1]+=h[i-1][j-1][0]-a[i]+1; } } */ for(int i=n+1>>1;i>=0;i--) { dp[n][i][0]=Mn(dp[n][i][0],dp[n][i][1]); dp[n][i][0]=Mn(dp[n][i][0],dp[n][i+1][0]); } for(int i=1,lm=n+1>>1;i<=lm;i++) printf("%d ",dp[n][i][0]); puts(""); return 0; }
自己原来还写了一个不是从 i-2 转移的,但带了一个 h[ i ][ j ][ 0/1 ] 表示当前的高度。这个 h[ ][ ][ ] 不参与转移,所以只是记录一下,不增加复杂度。但不知为何不对。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; int Mx(int a,int b){return a>b?a:b;} int Mn(int a,int b){return a<b?a:b;} const int N=5005; int n,a[N],dp[N][N][2],h[N][N][2]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); memset(dp,0x3f,sizeof dp); dp[0][0][0]=0; for(int i=1,lm=i+1>>1;i<=n;i++,lm=i+1>>1) for(int j=0;j<=lm;j++) { dp[i][j][0]=dp[i-1][j][0]; h[i][j][0]=a[i]; int w; if(a[i]>=h[i-1][j][1])w=a[i]-h[i-1][j][1]+1; else w=0; if(dp[i-1][j][1]+w<dp[i][j][0]) dp[i][j][0]=dp[i-1][j][1]+w, h[i][j][0]=Mn(h[i][j][0],h[i-1][j][1]-1);//Mn if(j) { dp[i][j][1]=dp[i-1][j-1][0]; h[i][j][1]=a[i]; if(h[i-1][j-1][0]>=a[i]) dp[i][j][1]+=h[i-1][j-1][0]-a[i]+1; } } for(int i=n+1>>1;i>=0;i--) { dp[n][i][0]=Mn(dp[n][i][0],dp[n][i][1]); dp[n][i][0]=Mn(dp[n][i][0],dp[n][i+1][0]); } for(int i=1,lm=n+1>>1;i<=lm;i++) printf("%d ",dp[n][i][0]); puts(""); return 0; }