H - String painter
HDU - 2476
The first line contains string A.
The second line contains string B.
The length of both strings will not be greater than 100.
zzzzzfzzzzz abcdefedcba abababababab cdcdcdcdcdcd
6 7
题意:
给出两个串s1和s2,一次只能将一个区间刷一次,问最少几次能让s1=s2
例如zzzzzfzzzzz,长度为11,我们就将下标看做0~10
先将0~10刷一次,变成aaaaaaaaaaa
1~9刷一次,abbbbbbbbba
2~8:abcccccccba
3~7:abcdddddcba
4~6:abcdeeedcab
5:abcdefedcab
这样就6次,变成了s2串了
第二个样例也一样
0
先将0~10刷一次,变成ccccccccccb
1~9刷一次,cdddddddddcb
2~8:cdcccccccdcb
3~7:cdcdddddcdcb
4~6:cdcdcccdcdcb
5:cdcdcdcdcdcb
最后竟串尾未处理的刷一次
就变成了串2cdcdcdcdcdcd
所以一共7次
题解:
(1)子问题分析:
其实这道题无非是两种情况,(1)a串与b串对于位置i字符是一样的,这样就直接用前一个字符的需要修改的次数就好了,当前i字母不用刷
(2)a串和b串对应位置不同,也就是当前位置是需要刷的,这样怎么刷?好像没有规律了,不过唯一可以降低步数的可能是存在t[i] == t[k],因为这种情况下可能是不用刷的正确的处理方法是通过分割,就是前0-k个采取最优的刷法,k+1~i个采取爆刷的方式,就是不管a串对应的如何,直接对bk+1~i位置采用最少的步数刷(也就是下面的dp[][]),然后对每个分割点取最小值,还是有点抽象,但是结果是正确的,以后在深究。所以在真正算之前还需要预处理b串区间爆刷的步数,也就是本问题需要做两次DP,先预处理,再真正DP
(2)状态:
dp[i][j]为str2从空刷为i~j的刷法,也可以想为对于同等区段的str1,不管str1是什么,直接刷为对应的str2串
ans[i] 从0~i需要刷的最小步数,使其和b串一样
(3)状态转移方程:
预处理:dp[i][j] = min(dp[i][j],(dp[i+1][k]+dp[k+1][j]));///i与k相同,寻找i刷到k的最优方案
求结果:ans[i] = min(ans[i],ans[j]+dp[j+1][i]);///寻找j来分割区间得到最优解
#include <bits/stdc++.h> using namespace std; char str1[105],str2[105]; int dp[105][105];///dp[i][j]为str2从空刷为i~j的刷法,也可以想为对于同等区段的str1,不管str1是什么,直接刷为对应的str2串 int ans[105],i,j,k,len; int main() { while(~scanf("%s%s",str1,str2)) { len = strlen(str1); memset(dp,0,sizeof(dp)); for(j = 0; j<len; j++) { for(i = j; i>=0; i--)///j为尾,i为头 { dp[i][j] = dp[i+1][j]+1;///先每个单独刷 for(k = i+1; k<=j; k++)///i到j中间所有的刷法 { if(str2[i]==str2[k]) dp[i][j] = min(dp[i][j],(dp[i+1][k]+dp[k+1][j]));///i与k相同,寻找i刷到k的最优方案 } } } for(i = 0; i<len; i++) ans[i] = dp[0][i];///根据ans的定义先初始化 for(i = 0; i<len; i++) { if(str1[i] == str2[i]) ans[i] = ans[i-1];///如果对应位置相等,这个位置可以不刷 else { for(j = 0; j<i; j++) ans[i] = min(ans[i],ans[j]+dp[j+1][i]);///寻找j来分割区间得到最优解 } } printf("%d\n",ans[len-1]); } return 0; }