1. 程式人生 > >bzoj 1032: [JSOI2007]祖碼Zuma

bzoj 1032: [JSOI2007]祖碼Zuma

 

區間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; }