P4170 [CQOI2007]涂色(区间dp)

题目传送门

题意: 给你一个长度为n的木板,你每次可以选择一个连续长度的木板将它染成同一种颜色,在同一个地方,后来的颜色可以覆盖之前的颜色。给你一个目标颜色的木板,问你最少需要花多少次才能染成目标木板。(颜色用26种大写字母来表示)

思路: 他让我们求如何涂到目标长度,我们可以反过来想,把整块木板涂成相同颜色,最后对答案加1,这就是答案。因为我们在一个相同颜色块上面涂别的颜色相当于把这个别的颜色涂回去。
我们用 f [ i ] [ j ] [ c ] f[i][j][c] f[i][j][c]表示将区间 [ i , j ] [i,j] [i,j]全部涂成颜色 c c c所需要的最少步数。那么我们枚举中转点 k k k和颜色 c c c,那么有

  • f [ i ] j ] [ c ] = m i n ( f [ i ] [ j ] [ c ] , f [ i ] [ k ] [ c ] + f [ k + 1 ] [ j ] [ c ] ) f[i]j][c]=min(f[i][j][c],f[i][k][c]+f[k+1][j][c]) f[i]j][c]=min(f[i][j][c],f[i][k][c]+f[k+1][j][c])

这里值得注意的是,对于区间 [ i , j ] [i,j] [i,j],我们不止可能由区间 [ i , k ] + [ k + 1 , j ] [i,k]+[k+1,j] [i,k]+[k+1,j]转化而来,还有可能由区间 [ i , j ] [i,j] [i,j]直接转化而来,即整个区间涂好某种颜色之后,再把整个区间改成另一种颜色。所以我们在上述转移的时候可以维护一个最小的 f [ i ] [ j ] [ c ] f[i][j][c] f[i][j][c],后面用这个最小值再去更新一遍 f [ i ] [ j ] [ c ] f[i][j][c] f[i][j][c]

最后面我们可以求出整段木板涂成某一种颜色的最小代价,这个最小代价 + 1 +1 +1就是从空木板涂成目标木板所需最小步数。

代码:

#include<bits/stdc++.h>
#define endl '\n'
#define null NULL
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define ll long long
#define int long long
#define pii pair<int,int>
#define ull unsigned long long
#define pdd pair<double,double>
#define lowbit(x) x&-x
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
char *fs,*ft,buf[1<<20];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read()
{
    
    
    int x=0,f=1;
    char ch=gc();
    while(ch<'0'||ch>'9')
    {
    
    
        if(ch=='-')
            f=-1;
        ch=gc();
    }
    while(ch>='0'&&ch<='9')
    {
    
    
        x=x*10+ch-'0';
        ch=gc();
    }
    return x*f;
}
using namespace std;
const int N=2e5+55;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const double eps=1e-6;
const double PI=acos(-1);
char s[N];
int f[111][111][30];
void solve()
{
    
    
    cin>>s+1;
    int n=strlen(s+1);
    memset(f,0x3f,sizeof f);
    for(int i=1; i<=n; i++)
        for(char c='A'; c<='Z'; c++)
            f[i][i][(int)(c-'A')]=(s[i]==c?0:1);
    for(int len=2; len<=n; len++)
    {
    
    
        for(int i=1; i+len-1<=n; i++)
        {
    
    
            int j=i+len-1;
            for(int k=i; k<=j-1; k++)
            {
    
    
                int mi=1e18;
                for(char c='A'; c<='Z'; c++)
                {
    
    
                    int p=(int)(c-'A');
                    f[i][j][p]=min(f[i][j][p],f[i][k][p]+f[k+1][j][p]);
                    mi=min(mi,f[i][j][p]);
                }
                for(char c='A'; c<='Z'; c++)
                {
    
    
                    int p=(int)(c-'A');
                    f[i][j][p]=min(f[i][j][p],mi+1);
                }
            }
        }
    }
    int res=inf;
    for(char c='A'; c<='Z'; c++)
        res=min(res,f[1][n][(int)(c-'A')]);
    cout<<res+1<<endl;
}
signed main()
{
    
    
//    int t;
//    cin>>t;
//    while(t--)
    solve();
    return 0;
}


猜你喜欢

转载自blog.csdn.net/Joker_He/article/details/109678473