区间涂色问题

Posted wujw11world

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了区间涂色问题相关的知识,希望对你有一定的参考价值。

  • 一眼区间dp
  • 设dp[i][j]为涂完i到j所需的最小次数
  • 当a[i]==a[j]时,dp[i][j] = min(dp[i+1][j-1]+1,min(dp[i+1][j],dp[i][j-1]));
  • 为什么是dp[i+1][j-1]+1,此时会产生一个异想天开的想法,就是取遍历一遍i+1到j-1这一段字符串,看是否有a[i]字符的出现,如果出现的话dp[i][j] = dp[i+1][j-1],就是上来先把i到j这一段全涂成a[i],然后去涂其他的,但这种做法不能保证在dp[i+1][j-1]-1的次数内正确涂满其余没涂的,例如,ABABA,dp[2][4] = 3,如果按照以上方法更新,dp[1][5] = 3,实际上dp[1][5] = 4。再来看dp[i][j-1]与dp[i+1][j],在涂a[i]或a[j]时直接把i-j涂完,不会像前面一样影响答案。
  • 当a[i]!=a[j]时,拆分区间就好
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
char a[55];
int dp[55][55];
int main()
    string s;
    cin>>s; n = s.length();
    for(int i = 1;i<=n;i++)
        for(int j = 1;j<=n;j++) 
            dp[i][j] = 51;
        
    
    for(int i = 1;i<=s.length();i++)
        a[i] = s[i-1];dp[i][i] = 1;
    

    for (int i = 1; i  <= n-1; i++) 
		if(a[i]!=a[i+1]) dp[i][i+1] = 2;
        else dp[i][i+1] = 1;
    
    
    for (int len = 3; len <= n; len++) 
			for (int i = 1; i + len - 1 <= n; i++) 
				int j = i + len - 1;
                if(a[i]==a[j])
                    dp[i][j] = min(dp[i+1][j-1]+1,min(dp[i+1][j],dp[i][j-1]));
                
                else
                    
                    for(int k = i;k<=j-1;k++)
                     //   if(i==1&&j==3)
                   //         cout<<dp[i][k]<<" "<<dp[k+1][j]<<" "<<dp[i][j]<<endl;
                    //    
                        dp[i][j] = min(dp[i][k]+dp[k+1][j],dp[i][j]);
                    
                
			
		
    cout<<dp[1][n];

CQOI2007 涂色

传送门

这道题才应该是标准的区间DP!

其实这个题用一种神奇的算法瞎写就能得80……一会可以附上参考代码……如果有神犇愿意帮助debug不胜感激……

考虑区间DP。用dp[i][j]表示将区间[i,j]涂好需要使用的最少的颜色种数。

既然如此,dp方程就很显然,因为毕竟区间dp的思想就是先算小区间再合并成大区间。所以我们先从小到大枚举区间长度,之后再枚举区间端点。然后,因为一个大区间可以看作由两个小区间合并而成(因为小区间的状态是全部被染好的,所以是可以直接合并成大区间的),所以我们枚举区间断点。

那么dp[i][j] = min(dp[i][k] + dp[k+1][j],dp[i][j]),其中k由i+1枚举到j即可。

还有一点就是,如果当前区间枚举的两个点所对应的元素相同,那么直接有dp[i][j] = min(dp[i][j],dp[i+1][j-1] + 1).这个也是很显然的。

既然如此就可以上代码了,很简短。(80pts的在前)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define rep(i,a,n) for(ll i = a;i <= n;i++)
#define per(i,n,a) for(ll i = n;i >= a;i--)
#define enter putchar(‘
‘)

using namespace std;
const int M = 105;
typedef long long ll;

int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < 0 || ch > 9)
    {
        if(ch == -) op = -1;
        ch = getchar();
    }
    while(ch >= 0 && ch <= 9)
    {
        ans *= 10;
        ans += ch - 0;
        ch = getchar();
    }
    return ans * op;
}

char s[100];
int dp[105][105],len;
int main()
{
    scanf("%s",s+1);
    len = strlen(s+1);
//    printf("%d
",len);
    memset(dp,127/3,sizeof(dp));
    rep(i,1,len) dp[i][i] = 1;
    rep(p,0,len)
    rep(i,1,len-p)
    {
        if(s[i] == s[i+1]) dp[i][i+p] = min(dp[i+1][i+p],dp[i][i+p]);
        if(s[i+p-1] == s[i+p]) dp[i][i+p] = min(dp[i][i+p],dp[i][i+p-1]);
        if(s[i] == s[i+p]) 
        {
            if(s[i] == s[i+1] && s[i+p-1] == s[i+p]) dp[i][i+p] = min(dp[i][i+p],dp[i+1][i+p-1]);
            else dp[i][i+p] = min(min(dp[i][i+p],dp[i+1][i+p-1]+1),min(dp[i+1][i+p],dp[i][i+p-1]));
        }
        dp[i][i+p] = min(dp[i][i+p],dp[i+1][i+p] + 1);
        dp[i][i+p] = min(dp[i][i+p],dp[i][i+p-1] + 1);
        dp[i][i+p] = min(dp[i][i+p],dp[i+1][i+p-1] + 2);
    }
    printf("%d
",dp[1][len]);
    return 0;
}

 

下面的是AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define rep(i,a,n) for(ll i = a;i <= n;i++)
#define per(i,n,a) for(ll i = n;i >= a;i--)
#define enter putchar(‘
‘)

using namespace std;
const int M = 105;
typedef long long ll;

int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < 0 || ch > 9)
    {
        if(ch == -) op = -1;
        ch = getchar();
    }
    while(ch >= 0 && ch <= 9)
    {
        ans *= 10;
        ans += ch - 0;
        ch = getchar();
    }
    return ans * op;
}

char s[100];
int dp[105][105],len;
int main()
{
    scanf("%s",s+1);
    len = strlen(s+1);
    memset(dp,127/3,sizeof(dp));
    rep(i,1,len) dp[i][i] = 1;
    rep(p,1,len-1)
    rep(i,1,len-p)
    {
        if(s[i] == s[i+p]) dp[i][i+p] = min(dp[i+1][i+p],dp[i+1][i+p-1]+1);
        else rep(k,i,i+p-1) dp[i][i+p] = min(dp[i][i+p],dp[i][k]+dp[k+1][i+p]);
    }
    printf("%d
",dp[1][len]);
    return 0;
}

 



以上是关于区间涂色问题的主要内容,如果未能解决你的问题,请参考以下文章

B1260 [CQOI2007]涂色paint 区间dp

BZOJ 1260--涂色(区间DP)

CQOI2007 涂色

BZOJ-1260涂色paint 区间DP

P4170 [CQOI2007]涂色(区间dp)

涂色「CQOI2007」