UVALive - 4394 String painter DP

来源:互联网 发布:网络监控员是做什么的 编辑:程序博客网 时间:2024/05/21 06:31

题目大意:给出两个字符串,可以对将第一个字符串进行操作,操作的过程是将第一个字符串中的一个连续的子串换成同一个字母,问至少需要多少次操作才能使第一个字符串变成第二个字符串

解题思路:这里两种解法:

第一个解法(比较繁琐):设dp[i]为扫描到字符串的第i个字符时需要进行操作的最小次数。这里有两种情况

1.str1[i] == str2[i],这样的话就不需要对第i个进行操作了,直接dp[i] = (i == 0 ? 0 : dp[i-1])

2.str1[i] != str2[i],从左到右扫描,如果str2[j] == str2[i],就从这个j处进行切割,将其分成两个部分,[0,j-1]和[j,i],这样的话dp[i] = min(dp[i],dp[j-1]+statu[j+1][i-1][str2[j]-'a']+1),还要考虑到j是0的情况,当j == 0,dp[i] = min(dp[i],statu[j+1][i-1][str2[j]-'a']+1)

这里解释一下这个statu数组和后面的1的意思,statu[i][j][k]表示将第一个字符串从第i个字符到第j个的字符都为k的子串变成相应的第二个字符串的子串需要操作的最少次数,为什么会从第i个字符到第j个字符都是字符k呢,因为头尾相同的情况下,可以将中间部分变成和头尾相同的字符,这样可以减少一次操作,所以后面要再statu后面加上了1,表示进行了一个前面所说的操作了,具体看代码,写得比较急,如果写错的话请见谅

#include<cstdio>#include<algorithm>#include<cstring>using namespace std;#define maxn 110#define INF 0x3f3f3f3fchar str1[maxn], str2[maxn];int statu[maxn][maxn][30], dp[maxn];int DP(int start, int end, int c) {if(start > end)return 0;if(statu[start][end][c] != INF)return statu[start][end][c];if(start == end) {if(str2[start] - 'a' == c)statu[start][end][c] = 0;elsestatu[start][end][c] = 1;return statu[start][end][c];}for(int i = start; i <= end - 1; i++)statu[start][end][c] = min(statu[start][end][c],DP(start,i,c)+DP(i+1,end,c));if(str2[start] == str2[end]) {if(str2[start] - 'a' == c)statu[start][end][c] = min(statu[start][end][c],statu[start+1][end-1][c]);elsestatu[start][end][c] = min(statu[start][end][c],DP(start+1,end-1,str2[start]-'a')+1);}return statu[start][end][c];}int main() {while(scanf("%s%s",str1, str2) != EOF) {int len = strlen(str2);memset(statu,0x3f,sizeof(statu));memset(dp,0x3f,sizeof(dp));for(int i = 0; i < len; i++) {if(str1[i] == str2[i]) {if(i == 0)dp[i] = 0;elsedp[i] = dp[i-1];}else {for(int j = i; j >= 0; j--) if(str2[i] == str2[j]) {if(j == 0)dp[i] = min(dp[i],DP(j+1,i-1,str2[i]-'a')+1);elsedp[i] = min(dp[i],dp[j-1]+DP(j+1,i-1,str2[i]-'a')+1);}}}printf("%d\n",dp[len-1]);}return 0;}
解法二:(区间DP)可以想将一个空字符串变成第二个字符串,看中间的操作需要进行多少次。用dp[i][j]表示将一个空字符串变成第二个字符串的第i个字符到第j个字符的子串的操作次数。

这里的转换就和上面的差不多,就不详写了,不懂请看代码。用ans[i]表示第一个字符串的前i个字符变成第二个字符串的前i个字符的操作次数。

首先ans[i] = dp[0][i],这是最差的状态

然后如果str1[i] == str2[i], ans[i] = min(ans[i], (i == 0 ? 0:ans[i-1]))

最后再进行区间分段,求出最优的方法,ans[i] = min(ans[i],ans[j] + dp[j+1][i])

#include<cstdio>#include<algorithm>#include<cstring>using namespace std;#define maxn 110#define INF 0x3f3f3f3fint dp[maxn][maxn], ans[maxn];char str1[maxn], str2[maxn];void DP(int i, int j) {if(i == j) {dp[i][j] = 1;return ;}dp[i][j] = dp[i+1][j] + 1;for(int k = i + 1; k <= j; k++) {if(str2[i] == str2[k])dp[i][j] = min(dp[i][j],dp[i+1][k]+dp[k+1][j]);}}int main() {while(scanf("%s%s", str1, str2) != EOF) {int len = strlen(str2);memset(dp,0,sizeof(dp));for(int i = 0; i < len; i++)for(int j = 0; i + j < len; j++) DP(j,i+j);for(int i = 0; i < len; i++) {ans[i] = dp[0][i];if(str1[i] == str2[i])ans[i] = min(ans[i],(i == 0 ? 0:ans[i-1]));for(int j = 0; j < i ; j++)ans[i] = min(ans[i],ans[j]+dp[j+1][i]);}printf("%d\n",ans[len-1]);}return 0;}


0 0
原创粉丝点击