HDU 2476 String painter(区间DP)

问题

hdu 2476 String painter- http://acm.hdu.edu.cn/showproblem.php?pid=2476

paint规则

  • 每一次paint操作,至少存在一个字符,从不匹配变得匹配;若操作区间 [ i , j ] [i, j] [i,j] 的首字符不属于这种情况,则首字符可以不操作,依次类推,区间 [ i , j ] [i, j] [i,j] 的首字符一定是从不匹配变得匹配;同理,区间的尾字符亦如此;因此,在目标串b中有 b [ i ] = b [ j ] b[i]=b[ j] b[i]=b[j]
  • 任意两次paint操作的区间端点不重叠。如果重叠,第一次paint对端点的操作明显是多余的,即可以缩小第一次操作的区间大小。因此,区间 [ i , j ] [i, j] [i,j] 操作可以转化为 [ i , k ] [i, k] [i,k] [ k + 1 , j ] [k+1, j] [k+1,j] 两个子问题。
  • k k k 的取值规则:
    • k = i k=i k=i,即仅考虑是否paint区间 [ i , j ] [i, j] [i,j] 的首字符,原问题转化为 [ i + 1 , j ] [i+1, j] [i+1,j]
    • k = n x t [ k ] k=nxt[k] k=nxt[k],即 paint 了区间 [ i , k ] [i, k] [i,k] 的所有字符,使得 a [ i , ⋯   , k ] = b [ i ] a[i,\cdots , k]=b[i] a[i,,k]=b[i],操作完成后, a [ i ] 、 a [ k ] a[i]、a[k] a[i]a[k] 实现匹配且 a [ i + 1 , ⋯   , k − 1 ] = b [ i ] a[i+1, \cdots , k-1]=b[i] a[i+1,,k1]=b[i],此时转化为 [ i + 1 , k − 1 ] [i+1, k-1] [i+1,k1] [ k + 1 , j ] [k+1, j] [k+1,j] 两个子问题
    • n x t [ k ] nxt[k] nxt[k]:在目标串b中, b [ k ] b[k] b[k] 右侧第一个与 b [ k ] b[k] b[k] 相同的字符的位置

方法

一次DP

状态设计

  • d p ( i , j , k ) dp(i, j, k) dp(i,j,k):源串A在区间 [ i , j ] [i,j] [ij] 内的所有字符是k时的最优解
  • k ∈ { 0 、 1 、 2 、 ⋯ 、 25 、 26 } k \in \{0、1、2、\cdots、25、26\} k{0122526}
    • 0 、 1 、 2 、 ⋯ 、 25 0、1、2、\cdots、25 01225 表示源串A在区间内的字符都是 a a a b b b ⋯ \cdots z z z
    • 26 26 26 表示源串A在区间内的字符就是读入的原始字符
  • 状态转移
    • 如果,仅paint首字符
      • 首字符相等 d p ( i , j , k ) = m i n ( d p ( i , j , k ) , d f s ( l + 1 , r , k ) ) dp(i, j, k) = min(dp(i, j, k), dfs(l+1, r, k)) dp(i,j,k)=min(dp(i,j,k),dfs(l+1,r,k))
      • 首字符不等 d p ( i , j , k ) = m i n ( d p ( i , j , k ) , 1 + d f s ( l + 1 , r , k ) ) dp(i, j, k) = min(dp(i, j, k), 1+dfs(l+1, r, k)) dp(i,j,k)=min(dp(i,j,k),1+dfs(l+1,r,k))
    • 否则, d p ( i , j , k ) = m i n ( d p ( i , j , k ) , 1 + d f s ( l + 1 , t − 1 , b [ s ] − ′ a ′ ) + d f s ( t + 1 , r , k ) ) dp(i, j, k) = min(dp(i, j, k), 1+dfs(l+1, t-1, b[s]-'a')+dfs(t+1, r, k)) dp(i,j,k)=min(dp(i,j,k),1+dfs(l+1,t1,b[s]a)+dfs(t+1,r,k))

代码【46MS】

/* hdu 2476 String Painter 区间dp*/
#include<bits/stdc++.h>
using namespace std;
const int MXN = 110;
const int inf = 0x7f7f7f7f;
int  n, nxt[MXN], dp[MXN][MXN][27];
char a[MXN], b[MXN];
int dfs(int l, int r, int ch){
	int &D = dp[l][r][ch];
	if(D < inf) return D;
	if(l > r) return D = 0;
	int s = l, t;
	for(t = l; t <= r; t = nxt[t]){
		if(s == t){
			if((ch==26?a[s]:(ch+'a')) == b[s]) D = min(D, dfs(l+1, r, ch));
			else D = min(D, 1+dfs(l+1, r, ch));
		}
		else D = min(D, 1+dfs(l+1, t-1, b[s]-'a')+dfs(t+1, r, ch)); 
	}
	return D;
}
int main(){
	int c[26];
	while(scanf("%s%s", a, b) == 2){
		memset(dp, inf, sizeof dp);
		n = strlen(a);
		for(int i = 0; i < 26; ++i) c[i] = n;
		for(int i = n-1; i >= 0; --i) nxt[i] = c[b[i]-'a'], c[b[i]-'a'] = i;
		printf("%d\n", dfs(0, n-1, 26));
	}
    return 0;
}

二次DP_1

2维状态

  • d p ( i , j ) dp(i, j) dp(i,j):区间 [ i , j ] [i, j] [i,j] 内每一个字符至少paint一次时的最优解【即不考虑源串a与目标串b的初始匹配情况】

1维状态

  • f ( i ) f(i) f(i) a [ i ] a[i] a[i] 是最后一个被paint次数为0的字符时的最优解

代码【31MS】

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e2+5;
char s[maxn],t[maxn];
int dp[maxn][maxn],n,f[maxn];
int main() {
    while (~scanf("%s%s",s+1,t+1)){
        n=strlen(s+1);
        for (int i=1;i<=n;i++)dp[i][i]=1;
        for (int len=2;len<=n;len++){
            for (int i=1;i<=n;i++){
                int j=i+len-1;
                if (j>n)break;
                dp[i][j]=dp[i+1][j]+1;
                for (int k=i+1;k<=j;k++){
                    if (t[i]==t[k])dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]);
                }
            }
        }
        for (int i=1;i<=n;i++){
            f[i]=dp[1][i];
            if (s[i]==t[i])f[i]=f[i-1];
            for (int j=1;j<i;j++)f[i]=min(f[i],f[j]+dp[j+1][i]);
        }
        printf("%d\n",f[n]);
    }
    return 0;
}

代码【0-15MS】

// hdu 2476 String painter
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define MXN 110
char A[MXN], B[MXN];
int dp[MXN][MXN], n, f[MXN], nxt[MXN];
int main() {
	int s, t, tmp;
	while (scanf("%s%s", A+1, B+1) == 2) {
		n = 0;
		while(A[n+1]) n++;
		for(int i = 1; i <= 256; ++i) f[i] = n+1;
		for(int i = n; i >= 1; --i) nxt[i] = f[B[i]], f[B[i]] = i;
		for(int i = 1; i <= n; ++i) dp[i][i] = 1;
		for(int len = 2; len <= n; ++len){
			for(int j = 1; j <= n; ++j){
				s = j, t = j+len-1;
				if(t > n) break;
				dp[j][t] = dp[j+1][t]+1;
				s = nxt[s];				
				while(s <= t){
					tmp = dp[j+1][s];
					if(s+1 <= t) tmp += dp[s+1][t];
					dp[j][t] = min(dp[j][t], tmp);
					s = nxt[s];
				}
			}
		}
		f[0] = 0;
		for(int i = 1; i <= n; ++i){
			f[i] = f[i-1] + (A[i] == B[i] ? 0 : 1);
			for(int j = 0; j < i; ++j){
				f[i] = min(f[i], f[j]+dp[j+1][i]);
 			}
		}
		printf("%d\n", f[n]);
	}
	return 0;
}

代码

// hdu 2476 String painter
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define MXN 150
char A[MXN], B[MXN];
int dp[MXN][MXN], n, f[MXN], nxt[MXN];
int main() {
	int s, t, tmp;
	while (scanf("%s%s", A+1, B+1) == 2) {
		n = 0;
		while(A[n+1]) n++;
		for(int i = 1; i <= 256; ++i) f[i] = n+1;
		for(int i = n; i > 0; --i) {
            nxt[i] = f[B[i]];
			f[B[i]] = i;
		}
		for(int i = 1; i <= n; ++i) dp[i][i] = 1;
		for(int len = 2; len <= n; ++len){
			for(int j = 1; j <= n; ++j){
				s = j, t = j+len-1;
				if(t > n) break;
				dp[j][t] = dp[j+1][t]+1;
				s = nxt[s];				
				while(s <= t){
					tmp = dp[j+1][s];
					if(s+1 <= t) tmp += dp[s+1][t];
					dp[j][t] = min(dp[j][t], tmp);
					s = nxt[s];
				}
			}
		}
        for(int i = n; i >= 0; --i) nxt[i] = A[i+1] == B[i+1] ? (i+1) : nxt[i+1];
		f[0] = 0;
		for(int i = 1; i <= n; ++i){
            if(A[i] == B[i]){
                f[i] = f[i-1];
                continue;
            }
            else f[i] = dp[1][i];
			for(int j = nxt[0]; j < i; j = nxt[j]){
				f[i] = min(f[i], f[j-1] + dp[j+1][i]);
 			}
		}
		printf("%d\n", f[n]);
	}
	return 0;
}

二次DP_2

3维状态

  • dp(i, j, k)的定义:源串A在区间(i,j)内的所有字符都是k时的最优解

2维状态

  • f(i, j)的定义:区间(i,j)的最优解

代码【202MS】

#include<bits/stdc++.h>
using namespace std;
#define MXN 110
#define INF 0x3f3f3f3f
int N, nxt[MXN], C[256], dp[MXN][MXN][26], f[MXN][MXN];
string A, B;
int main(){
	int s, t, tmp;
	while(cin >> A >> B){
		N = A.size();
		for(int i = 0; i < 256; i++) C[i] = N;
		for(int i = N-1; i >= 0; i--){
			nxt[i] = C[B[i]];
			C[B[i]] = i;
		}
		memset(dp, INF, sizeof dp);
		memset(f, INF, sizeof f);	
		for(int i = 1; i <= N; i++){ // 长度	
			for(int j = 0; j <= N - i; j++){ // 起点
				for(char k = 'a'; k <= 'z'; k++){
					s = j, t = j+i-1;
					while(s <= t){
						tmp = B[j] == k ? 0 : 1;
						if(j+1 <= s-1) tmp += dp[j+1][s-1][B[j]-'a'];
						if(s+1 <= t) tmp += dp[s+1][t][k-'a'];
						dp[j][t][k-'a'] = min(dp[j][t][k-'a'], tmp);
						s = nxt[s];
					}
				}
			}
		}
		for(int i = 1; i <= N; i++){
			for(int j = 0; j <= N-i; j++){
				s = j, t = j+i-1;
				while(s <= t){
					if(B[j] == A[j] && B[s] == A[s])
						tmp = (j+1 <= s-1) ? f[j+1][s-1] : 0;
					else{
						tmp = (j+1 <= s-1) ? dp[j+1][s-1][B[j]-'a'] : 0;
						tmp += 1;
					}
					if(s+1 <= t) tmp += f[s+1][t];
					f[j][t] = min(f[j][t], tmp);
					s = nxt[s];
				}
			}
		}
		printf("%d\n", f[0][N-1]);
	} 
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jpphy0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值