String painter HDU - 2476(双重区间dp or 区间dp加dp)

题目

There are two strings A and B with equal length. Both strings are made up of lower case letters. Now you have a powerful string painter. With the help of the painter, you can change a segment of characters of a string to any other character you want. That is, after using the painter, the segment is made up of only one kind of character. Now your task is to change A to B using string painter. What’s the minimum number of operations?
Input
Input contains multiple cases. Each case consists of two lines:
The first line contains string A.
The second line contains string B.
The length of both strings will not be greater than 100.
Output
A single line contains one integer representing the answer.
Sample Input

zzzzzfzzzzz
abcdefedcba
abababababab
cdcdcdcdcdcd

Sample Output

6
7

题意

给你一把小刷子,区间任选,从左到右,选择一种字母刷一遍,最后刷成第二个串的样子,问最少刷几次。

解释

由于直接考虑很复杂,先假设是空串刷成第二个串,区间dp得到dp数组再借助这个基础上,再次dp出答案。
dp[1][j]代表i~j最少的刷的次数,以i ~ j的元素作为参考系。
考虑i~j区间第一个字符的刷法,来递推。
如果第一个字符后面的字符没有与它相同的,dp[i][j] = dp[i+1][j] +1;
如果有 a[i] == a[k]条件下 dp[i][j] = min(dp[i][j], dp[i+1][k] +dp[k+1][j]);(因为有相同,所以对应的情况是,i对应的字符相当于是第一遍刷的,刷到k位置,数值上等于dp[i+1][k],这样更新完所有符合的k对应的情况,得到dp[i][j]最小值)
由于只要匹配答案肯定小于等于不匹配的情况,所以下面代码前面写出来没有分支。
按照区间dp得到空串到串2的各个区间的最小刷次数。
再考虑串1到串2借助dp数组来得到最终答案,因为串1有些部分和串2是相同的,理论上会出现可以少更新,得到更优解的可能性。
dpt[i]代表1~i的最小刷次数。
考虑1~i区间最后一个字符的刷法(考虑第一个的刷法应该也是可行的,因为刷一次时左刷还是右刷对单次产生效果不变)
如果串1第i字符与与串2第i字符相同 那么刷完1~i与刷完1 ~ i-1答案应该一样,因为i位置不需要刷,本来就相同,故 dpt[i] = dpt[i-1];
但如果不相同,dpt[i] = min(dpt[i], dpt[k]+ dp[k+1][j]);枚举所有位置.
dpt[k]+ dp[k+1][j]根据上面的解释是1~k的最小刷次数加k+1到j的最小刷次数,枚举所有位置得到最小结果。为什么是dpt[k]呢?因为1 ~ K中有些就是两串相等的,可能会有更优的结果,这也是我们二次dp的原因。本来写到这我就结束了 可是我自己问的为什么是dpt[k]?那么为什么后半段加的是dp[k+1][j],而不是dpt有关的最优答案。
从最大区间开始想,1~ i最优解等于更新过的1~k最优解加上k到i的最优解,讲道理才是1 ~ i的最优解。明显考虑了k ~i两串相同部分后的新答案必然小于等于k到i的未考虑相同部分的答案。所以代码3的写法是对的。
但是代码1的写法为什么也可以,问题是同一个区间,空串刷成串2的状态A和串一考虑相同部分不刷的的状态B,哪种更优是不一定的。
比如mbn 与 bbb 状态A反而优于B,所以我们对每个区间都要选择两者更优的选择。产生更优的只有有字符相同的子区间会比第一次区间dp可能产生更优解。所以其实不用像代码1把把所有点都枚举一遍(从某种角度上来说,代码1的状态方程思路不太对,但能更新所有的正确状态方程的情况,所以能对),因此只用对有可能产生更优解的区间作为子状态拿来更新就好了,这时候dpt存的其实是经过两次择优后的答案最优解,dpt[i] = min(dpt[i], dpt[cp[k]] + dp[cp[k]+1][i]);(cp[k]对应的值是到i之前所有相同字符的位置的值),
到这里这个问题差不多清楚了。
代码1

#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 103
int const inf = 0x3f3f3f3f;
using namespace std;
int dp[maxn][maxn];
int dpt[maxn];
char str1[maxn], str2[maxn];
int main(){
	int n, i, j, k;
	while(~scanf("%s", str1+1)){
		scanf("%s", str2+1);
		n = strlen(str1+1);
		memset(dp, 0, sizeof(dp));
		for(i = n; i >= 1; i--){
			for(j = i; j <= n; j++){
				dp[i][j] = dp[i+1][j] + 1;
				for(k = i+1; k <= j; k++){
					if(str2[k] == str2[i]) 
						dp[i][j] = min(dp[i][j], dp[i+1][k] + dp[k+1][j]);
				}		
			}
		}
		memset(dpt, 0, sizeof(dpt));

	for(i = 1; i <= n; i++){
			if(str1[i] != str2[i]){
				dpt[i] = dp[1][i];
				for(k = 1; k < i; k++)
				dpt[i] = min(dpt[i], dpt[k] + dp[k+1][i]);
			}	
			else
				dpt[i] = dpt[i-1];
			
		}
		printf("%d\n", dpt[n]);
	}	
	return 0;
} 

代码2

#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 103
int const inf = 0x3f3f3f3f;
using namespace std;
int dp[maxn][maxn];
int dpt[maxn];
int cp[maxn];
char str1[maxn], str2[maxn];
int main(){
	int n, i, j, k;
	while(~scanf("%s", str1+1)){
		scanf("%s", str2+1);
		n = strlen(str1+1);
		memset(dp, 0, sizeof(dp));
		for(i = n; i >= 1; i--){
			for(j = i; j <= n; j++){
				dp[i][j] = dp[i+1][j] + 1;
				for(k = i+1; k <= j; k++){
					if(str2[k] == str2[i]) 
						dp[i][j] = min(dp[i][j], dp[i+1][k] + dp[k+1][j]);
				}		
			}
		}
		memset(dpt, 0, sizeof(dpt));
 	int cont = 1;
 	for(i = 1; i <= n; i++)
 	if(str1[i] == str2[i]){
 		cp[cont++] = i;
	 }
	for(i = 1; i <= n; i++){
			if(str1[i] != str2[i]){
				dpt[i] = dp[1][i];
				for(k = 1; k < cont && cp[k] < i; k++)
				dpt[i] = min(dpt[i], dpt[cp[k]] + dp[cp[k]+1][i]);
			}	
			else
				dpt[i] = dpt[i-1];
			
		}
		printf("%d\n", dpt[n]);
	}	
	return 0;
} 

代码3

#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 103
int const inf = 0x3f3f3f3f;
using namespace std;
int dp[maxn][maxn];
int dpt[maxn][maxn];
char str1[maxn], str2[maxn];
int main(){
	int n, i, j, k;
	while(~scanf("%s", str1+1)){
		scanf("%s", str2+1);
		n = strlen(str1+1);
		memset(dp, 0, sizeof(dp));
		for(i = n; i >= 1; i--){
			for(j = i; j <= n; j++){
				dp[i][j] = dp[i+1][j] + 1;
				for(k = i+1; k <= j; k++){
					if(str2[k] == str2[i]) 
						dp[i][j] = min(dp[i][j], dp[i+1][k] + dp[k+1][j]);
				}		
			}
		}
		memset(dpt, 0, sizeof(dpt));

	for(i = n; i >= 1; i--){
		for(j = 1; j <= n; j++){
			if(str1[j] != str2[j]){
				dpt[i][j] = dp[i][j];
				for(k = i; k < j; k++)
				dpt[i][j] = min(dpt[i][j], dpt[i][k] + dpt[k+1][j]);
			}	
			else
				dpt[i][j] = dpt[i][j-1];
		}
	}
		printf("%d\n", dpt[1][n]);
	}	
	return 0;
} 
发布了52 篇原创文章 · 获赞 2 · 访问量 866

猜你喜欢

转载自blog.csdn.net/qq_44714572/article/details/103102155
今日推荐