2022 Robocom CAIP国赛 第四题 变牛的最快方法

原题链接:

PTA | 程序设计类实验辅助教学平台

 

题面:

 

12cbe7c6804f5b9a69962a693970c1ad.png

 

c58ee5f279782287cf3dc36745dda901.png

这里问的是把任意一种动物的图像变成牛的方法…… 比如把一只鼠的图像变换成牛的图像。方法如下:

  • 首先把屏幕上的像素点进行编号;
  • 然后把两只动物的外轮廓像素点编号按顺时针记录下来;
  • 用最少的变换次数将鼠的轮廓变成牛的 —— 这里仅允许对鼠的轮廓进行 3 钟操作:
  1. 插入一个像素编号
  2. 删除一个像素编号
  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;
}

 

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值