HDU-2476-String painter
这道题区间dp。
这道题目对我太不友好了。。。
维护答案的时候还是写了很久。。
啊啊啊。
这道题目给你两个序列。
你有一个画家助手。每次可以把连续的字符渲染成同一个字符。
问第一个序列A转化为B最少需要多少次。
我们这样考虑。dp[i][j]代表i~j字符转化的最少次数
我们先把一个空白的字符串转化成B最少需要多少次。
然后再来对比A和B。
维护答案。
初始化dp[i][j] = j - i + 1;//代表有多少个字符串就是最大操作次数
将空白字符转化为B的次数。
区间dp常用套路:
枚举长度j,i从后往前。
区间[i, j]内。枚举分界点。如果当前分界点的字符与i所指向的字符相等。
那么我们只需要改变i+1直到j即可。
dp[i][j] = min(dp[i][j], dp[i + 1][k] + dp[k + 1][j]);
之后维护ans[]
首先初始化一下ans[];
ans[i] = dp[0][i];
从第一个字符开始的值。index = 0;
之后开始比较
如果同一个索引位置上面的字符相等。
那么就代表这个地方的索引不需要被渲染。所以只需要渲染前面就可以了
ans[i] = ans[i - 1];
这个地方注意一下边界。如果i==0的话直接赋值为0就行,以免越界。特判一下
其余情况。
枚举0~i的范围即可。找到最佳切割点。
ans[i] = min(ans[i], ans[j] + dp[j + 1][i]);
当前ans[i]可能不是最佳的值。
有人可能会问了。在前面操作空白串处理dp的时候已经得到了最佳答案,为啥这里需要再维护一次。
是因为上面的if条件可能更改了ans[i],(对应于同一处的索引字符相等的情况,这种时不需要处理的),比如上面直接不处理ans[i] = 0的情况。所以最后需要再次维护(这里其实我卡了很久emmm,当时没想通。。。wa了好多次。。。)
代码部分:
#include <bits/stdc++.h>
#define mst(a, n) memset(a, n, sizeof(a))
using namespace std;
const int N = 1e2 + 10;
string s1, s2;
int dp[N][N];
int ans[N];
int main()
{
while (cin >> s1 >> s2)
{
int n = s1.size();
mst(dp, 0);
for (int j = 0; j < n; j++)
{
for (int i = j; i >= 0; i--)
{
dp[i][j] = dp[i + 1][j] + 1;
for (int k = i + 1; k <= j; k++)
{
if (s2[i] == s2[k])
{
dp[i][j] = min(dp[i][j], dp[i + 1][k] + dp[k + 1][j]);
}
}
}
}
for (int i = 0; i < n; i++)
{
ans[i] = dp[0][i];
}
for (int i = 0; i < n; i++)
{
if (s1[i] == s2[i])
{
ans[i] = !i ? 0 : ans[i - 1];
}
else
{
for (int j = 0; j < i; j++)
{
ans[i] = min(ans[i], ans[j] + dp[j + 1][i]);
}
}
}
cout << ans[n - 1] << endl;
}
return 0;
}