原题链接:
题面:
这里问的是把任意一种动物的图像变成牛的方法…… 比如把一只鼠的图像变换成牛的图像。方法如下:
- 首先把屏幕上的像素点进行编号;
- 然后把两只动物的外轮廓像素点编号按顺时针记录下来;
- 用最少的变换次数将鼠的轮廓变成牛的 —— 这里仅允许对鼠的轮廓进行 3 钟操作:
- 插入一个像素编号
- 删除一个像素编号
- 更改一个像素编号
输入格式:
输入分别在两行中给出两种动物的轮廓像素点编号,编号为 (0,106] 区间内的整数,允许重复。轮廓以编号 −1 结尾,这个编号不算在轮廓内。题目保证每种动物的轮廓包含不超过 1000 个像素点。
输出格式:
在第一行中输出从第一只动物变换成第二只动物需要的最少变换次数。
在第二行中顺次描述对第一只动物轮廓的每个像素所作的操作:
- 如果这个像素被删除,则在对应位置输出 0
- 如果这个像素被改变,则在对应位置输出 1
- 如果这个像素不变,则在对应位置输出 2
- 如果这个像素前面或者后面插入了一个像素,则在插入的位置输出 3
答案可能不唯一,输出任何一种可能的解都可以。行首尾和数字间均无空格。
输入样例:
13 5 6 20 2 20 1 13 9 20 3 28 3 34 6 25 233 -1 3 5 6 20 6 20 3 5 9 3 9 20 3 6 6 25 233 -1
输出样例:
8 122212112023121222
样例解释:
1、13 更改为 3,随后 5、6、20 不变
2、2 更改为 6,下一个 20 不变
3、1 更改为 3
4、第二个 13 更改为 5,随后 9 不变
5、删除下一个 20,后面的 3 不变
6、在 28 的前面插入 9
7、28 更改为 20,后面的 3 不变
8、34 更改为 6,后面的 6、25、233 不变
解题思路:
设dp[i][j]为A的前i位转化为B的前j位所需要的最少步数。
当A[i] == B[j]时,dp[i][j] = dp[i - 1][j - 1];
当A[i] != B[j]时,有以下三种情况:
dp[i - 1][j] + 1,删除,将A的最后一个字符删除
dp[i][j - 1] + 1,插入,在B的最后插入A的最后一个字符
dp[i - 1][j - 1] + 1,替换,将B的最后一个字符替换为A的最后一个字符
取最小即可。
至于输出每一位的操作,我们在求dp表的过程中就可以顺便记录每一步的转换操作,开一个二维数组edit来保存每一步状态转移。然后我们可以用递归的方式还原操作路径。具体实现细节详见代码。
代码(CPP):
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e3 + 10;
const int INF = 0x3fffffff;
const int mod = 1000000007;
int a[maxn], b[maxn];
int dp[maxn][maxn];
int edit[maxn][maxn];
vector<int> path;
int n, m;
void dfs(int i, int j) {
if (i == 0 && j == 0) {
return;
}
if (edit[i][j] == 0) {
dfs(i - 1, j);
} else if (edit[i][j] == 1) {
dfs(i - 1, j - 1);
} else if (edit[i][j] == 2) {
dfs(i - 1, j - 1);
} else {
dfs(i, j - 1);
}
path.push_back(edit[i][j]);
}
void DP() {
// 初始化
for(int i = 0; i <= n; i++) {
dp[i][0] = i;
edit[i][0] = 0;
}
for (int i = 0; i <= m; i++) {
dp[0][i] = i;
edit[0][i] = 3;
}
// 求出最短操作数,并记录状态转移路径
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i] == b[j]) {
dp[i][j] = dp[i - 1][j - 1];
edit[i][j] = 2;
} else {
int op = 3;
dp[i][j] = dp[i][j - 1] + 1;
if (dp[i][j] > dp[i - 1][j] + 1) {
op = 0;
dp[i][j] = dp[i - 1][j] + 1;
}
if (dp[i][j] > dp[i - 1][j - 1] + 1) {
op = 1;
dp[i][j] = dp[i - 1][j - 1] + 1;
}
edit[i][j] = op;
}
}
}
cout << dp[n][m] << endl;
// 反推出具体操作
dfs(n, m);
// path[1] = 1;
for (int i = 0; i < path.size(); i++)
{
cout << path[i];
}
}
void solve() {
int x;
while (cin >> x, x != -1) {
n++;
a[n] = x;
}
while (cin >> x, x != -1) {
m++;
b[m] = x;
}
DP();
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout << fixed;
cout.precision(18);
solve();
return 0;
}