[CQOI2007]塗色paint dp(區間)

【問題描述】

  假設你有一條長度爲5的木版,初始時沒有塗過任何顏色。你希望把它的5個單位長度分別塗上紅、綠、藍、綠、紅色,用一個長度爲5的字符串表示這個目標:RGBGR。
  每次你可以把一段連續的木版塗成一個給定的顏色,後塗的顏色覆蓋先塗的顏色。例如第一次把木版塗成RRRRR,第二次塗成RGGGR,第三次塗成RGBGR,達到目標。
  用盡量少的塗色次數達到目標。

【輸入格式】

  輸入僅一行,包含一個長度爲n的字符串,即塗色目標。字符串中的每個字符都是一個大寫字母,不同的字母代表不同顏色,相同的字母代表相同顏色。

【輸出格式】

  僅一行,包含一個數,即最少的塗色次數。

【輸入樣例】

【輸入1】
  AAAAA

【輸入2】
  RGBGR

【輸出樣例】

【輸出1】
  1

【輸出2】
  3

【數據範圍】

40%的數據滿足:1<=n<=10
100%的數據滿足:1<=n<=50

————————————————————————————————————————————————

最開始拿到這個題的時候我還做不出來,今天回來看了一下,感覺還是很有收穫的。總結成下面這一句話:

dp方程和遞歸實際上有着密不可分的關係(這是顯然的ORZ)。

實際上想說的就是遞歸思考是很有助於理解狀態轉移方程的!
下面來說一下思路。可以發現如果當前區間左右的顏色都是一樣的,那麼直接一筆塗完整個區間。因爲兩邊始終都要塗,肯定比分兩次塗要好,要不要+1取決於這個顏色和目標顏色是不是一樣的。如果兩端不一樣的話就枚舉中間一個點將這個區間剖分開來就轉移。

就完了,剩下的靠遞歸理解。時間複雜度O(n^3*c),c表示顏色的數量。

AC代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define inf 105
using namespace std;
const int maxn=55;

char s[maxn];
int N,tar[maxn];
int f[maxn][maxn][30],c[30],cnt;
bool vis[30];

void _scanf(char &x)
{
    x=getchar();
    while(x<'A'||x>'Z') x=getchar();
}
void data_in()
{
    gets(s+1);
    N=strlen(s+1);
    for(int i=1;i<=N;i++)
        tar[i]=s[i]-'A'+1,vis[s[i]-'A'+1]=1;
    for(int i=1;i<=26;i++)
        if(vis[i]) c[++cnt]=i;
}
void dp()
{
    for(int i=1;i<=N;i++)
    for(int k=1;k<=cnt;k++)
        f[i][i][c[k]]=c[k]==tar[i]?0:1;
    for(int len=1;len<N;len++)
    for(int i=1;i+len<=N;i++)
    for(int k=1;k<=cnt;k++)
    {
        int j=i+len,ii=i,jj=j;
        if(tar[i]==tar[j])
        {
            ii++;
            while(tar[ii]==tar[ii-1]&&ii<=j) ii++;
            jj--;
            while(tar[jj]==tar[jj+1]&&i<=jj) jj--;
            f[i][j][c[k]]=f[ii][jj][tar[i]]+(tar[i]==c[k]?0:1);
        }
        else
        {
            int tmp=inf;
            for(int d=i;d<j;d++)
                tmp=min(tmp,f[i][d][c[k]]+f[d+1][j][c[k]]);
            f[i][j][c[k]]=tmp;
        }
    }
}
void work()
{
    dp();
    int ans=inf;
    for(int i=1;i<=cnt;i++)
        ans=min(ans,f[1][N][c[i]]);
    printf("%d\n",ans+1);
}
int main()
{
    freopen("test.in","r",stdin);
    freopen("test.out","w",stdout);
    data_in();
    work();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章