bzoj 1032: [JSOI2007]祖碼Zuma
阿新 • • 發佈:2018-12-29
區間DP 多做多練
/************************************************************** Problem: 1032 User: lxy8584099 Language: C++ Result: Accepted Time:148 ms Memory:2008 kb ****************************************************************/ /* 區間DP 相同的顏色縮成一堆 記錄其左端點和右端點 不要忘記最後一堆的R為n。。。 初始化 如果左端點=右端點 則需要兩個同色球 否則一個就夠了 dp[i][j]表示 消去第i堆到第j堆需要的最少珠子(注意是堆! 轉移顯然有 dp[i][j] =min{ dp[i][k] + dp[k+1][j] } 但是遇到消去後 左右能繼續消去的要進行判斷處理*/ #include<cstdio> #include<cstring> #define min(a,b) ((a>b)?(b):(a)) using namespace std; const int N=550; int dp[N][N],n,top,col[N],L[N],R[N]; void Init() { scanf("%d",&n); for(int i=1,x;i<=n;i++) { scanf("%d",&x); if(i==1||x!=col[top]) col[++top]=x,L[top]=i,R[top-1]=i-1; } R[top]=n; // 不要忘記。。 } void Solve() { memset(dp,0x3f,sizeof(dp)); for(int i=1;i<=top;i++) if(L[i]==R[i]) dp[i][i]=2; else dp[i][i]=1; //預處理長度為 1 的堆 for(int l=2;l<=top;l++) // 列舉現在處理堆的長度 { for(int i=1,j=i+l-1;i<=top-l+1;++i,j=i+l-1) // 列舉區間左端點 { for(int k=i;k<j;k++) // 列舉dp中間點 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]); if(col[i]==col[j]&&i+1<=j-1) // 如果左端點 右端點顏色相同 就可以考慮讓它們自己消失 { if(L[i]==R[i]&&L[j]==R[j]) dp[i][j]=min(dp[i][j],dp[i+1][j-1]+1); // 左右都只有一個 就需要還加一個 else dp[i][j]=min(dp[i][j],dp[i+1][j-1]); // 否則就可以自己消失 } } } printf("%d\n",dp[1][top]); } int main() { Init(); Solve(); return 0; }