动态规划训练11 [String painter HDU - 2476]



题意:


我认为这是一道比较难的问题,自己想了很久,没有想出来怎么做,可能是因为思维僵化吧,一直在想怎么直接的由A变到B,事实上,可以有中间桥梁连接A和B,他们之间的关系可能往往没有我们想的那么简单。

依最简单的思路,我们定义ans[i]代表区间[1...i]处,A直接变到B所需的最少次数。那么状态转移方程可以这样写

有几种情况

(1)如果strA[i] == strB[i]的话,那么存在着一个转移方程就是 ans[i] = min(ans[i],ans[i-1]);

(2)否则的话,那么第i个位置的字符,一定会被刷掉,此外,刷掉第i个位置的区间长度可能大于1,假设这个区间刷掉了区间[x,i]内的数,并把区间strA[x...i]内的数都刷成了strB[i]

这样的话就相当于把区间[x...i]由空白串直接变成strB[x...i],剩下的区间[1...x-1]可以用ans[x-1]转移过来

也就是说,转移方程写成ans[i] = min(ans[i],ans[x-1] + dp[x][i]),dp[x][i]表示从空白串直接变成strB[x][i]所需要用的最小次数。

下面我们的重要目标就是求出dp[i][j]来

其实到这里还是不太好想。

最右边的字符strB[j]一定是最先被刷出来的

(1)如果strB[i] == strA[j],那么在一开始刷刷出strB[j]的时候,顺手就可以把strB[i]也给刷出来,所以不需要额外的费用

dp[i][j] = dp[i+1][j] 

(2)然后我们把区间分成两块进行转移(非常常规的想法)

dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]);


代码(注意我在前面的区间写的是全闭,而在代码里的区间写法是左闭右开):

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAX = 106;
char strA[MAX];
char strB[MAX]; 
int dp[MAX][MAX];
int ans[MAX];
int n;
int main(){
	while(scanf("%s %s",strA,strB) != EOF){
		memset(dp,0,sizeof(dp));
		memset(ans,0,sizeof(ans));
		n = strlen(strA);
		for(int i = 0;i < n;i++) dp[i][i+1] = 1;
		for(int len = 2;len <= n;len++){
			for(int i = 0;i + len <= n;i++){
				dp[i][i+len] = dp[i+1][i+len] + (strB[i] == strB[i+len-1]?0:1);
				for(int j = i+1;j < i+len;j++){
					dp[i][i+len] = min(dp[i][i+len],dp[i][j]+dp[j][i+len]);
				} 
			}
		}
		ans[0] = strA[0] == strB[0] ? 0:1;
		for(int i = 1;i < n;i++){
			ans[i] = dp[0][i+1];
			if(strB[i] == strA[i])
				ans[i] = min(ans[i],ans[i-1]);
			for(int j = 0;j < i;j++){
				ans[i] = min(ans[i],ans[j] + dp[j+1][i+1]);
			}
		}
		cout<<ans[n-1]<<endl;
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值