【题解·面向小学生OJ系列】codeforces : E. Cleaning Robot

【题目链接】

https://codeforces.com/contest/1739/problem/E

【题目大意】

有一个长廊,可以看成是2行N列的小格子组成的。有些小格子是脏的,现在我们使用机器人进行清扫。
机器人总是清扫离它最近的那个小格子。然后继续清扫那些还是脏的小格子。机器人从(1,1)开始进行清扫。*(长廊左上角是(1,1),右下角是(2,N),括号是(行数,列数))。
但是这个机器人有个Bug,当同时有几个点离他的距离是一样近的时候,它就会死机。我们现在有两个点分别是 ( x 1 , y 1 ) ( x 2 , y 2 ) (x_1,y_1)(x_2,y_2) (x1,y1)(x2,y2)距离计算公式是 ∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ |x_{1}-x_{2}|+|y_{1}-y_{2}| x1x2+y1y2。当机器人和这其中一些点距离一样,我们需要事先(启动机器人前)取出这些点。
现在,我们求,在机器人不会死机的状态下。机器人最多可以清扫多少点,输出。

【题目详解】

首先,我们来看看一种情况。
这张图我们有两个脏点,绿色机器人是我们开始启动机器人的点。请添加图片描述
我们发现,事实上,你可以移动这个绿色的机器人,比如移动到红色。从绿色出发的小机器人,和从红色出发的小机器人,获得的答案是一样的,为1。因为图中(1)(2)两个点是冲突的。

现在,我们来看这种情况:
请添加图片描述
绿色小机器人可以相当于从黄色出发吗?不可以。因为从黄色出发,那么对于(1)(2)两个脏节点的,距离,就被改变了。

所以,我们想到了动态规划。
首先我们有两个行,上面那个行用0表示,下面那个行用1表示。现在绿色机器人在j列。

定义dp[i][j]意思是,机器人在(i,j)位置出发,最大能清扫的灰尘数量。并且(i,j)位置地下,我们默认下面没有灰尘。(就是题目的那个问题啦)
那么dp[i][j]现在的想法是dp[i][j]=ch[i][j+1]+dp[i][j+1](就是上图这种)
ch[行][列]是我们读入的灰尘数据,因为dp[i][j]本身不包含(i,j)自己的脏灰,所以我们认为,我们先去(i,j)吃掉脏灰(如果有的话)。在从这里出发,即dp[i][j]

让我们继续往下想之前,先看看这个样例:

8 
00001101
01000110

它的输出应该是5,而不是4。
我们拿掉的不是因为第二行,最先遇到的1冲突,而是直接把它拿掉。
即现在地图变为:

8 
00001101
00000110

可见,就算不冲突,也应该,拿掉这个,获得最大的机器人清扫值。

现在,我们来按照“题目要求”,想想。假设,我们已经计算完了dp[*][j+1](从j+1列到n列,所有可能出发到n列,满足题意的最大清扫灰尘数目,也就是不会出现故障的清扫法)

那么,下图红色数字代表,从这里出发,最大能吃的灰尘。一个是4,一个是3,先自己勾画勾画感受一下。
请添加图片描述
绿色机器人,只能选择4。我们上文说过了,机器人横向移动。

如果小机器人下面有个1(脏灰)呢?
即:
请添加图片描述
那么你有两种方法(注意,我们说过了,小机器所在的位置,默认就是没有脏灰),第一种,把下面的那个(5)号脏灰拿掉,那么就算dp[0][j+1]状态。要不我不拿掉,就从(5)开始吃,那么就是,先吃五,然后再看dp[1][j+1]的状态。

所以,如果(i,j)对面有1的话,(1)要么事先拿走对面的1,(2)要么考虑对面的1。

仔细想想,对于(2)依旧有一些限制。请添加图片描述
如果是这样,那么(1,j)和(0,j+1)只能二选一了。如果选择了(1,j)的话,直接使用dp[1][j+1]是肯定不对的。因为有可能它包含了,(0,j+1)这个脏点,我们dp可没有保证,一定不会吃它上面的脏灰。所以我们不从dp[1][j+1]走,而从dp[1][j+2]走,这样,j+2列,肯定不会包含j+1列,怎么吃的方法。(我们的dp定义,从j这个列,一直到n,j+1不包含),对于j+1列,我们绝对不考虑(0,j+1)这个元素内的脏灰,就算有,也应该提前被手动拿走,这样才符合题意。然后从单独考虑(1,j+1)有没有脏灰,Over,这下正确了。

对于每一个脏灰,动态规划都有可能考虑到取用和不取用,所以这样的想法符合题意,应该是正确的。

给出如果对面有脏灰的转移方程:dp[j][i] = max(dp[j][i + 1] + (ch[j][i + 1] - '0'), 1 + (ch[j ^ 1][i + 1] - '0') + dp[j ^ 1][i + 2] + (ch[j ^ 1][i + 2] - '0'));

这样,我们有了完整的解题思路,注意开始和边界,写代码吧!

参考代码:

#include <bits/stdc++.h>

#include <iostream>
using namespace std;
int n;
#define Max_N 200000
char ch[3][Max_N + 5];
int dp[3][Max_N + 5];
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin >> n;
    for (int i = 0; i < 2; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> ch[i][j];
        }
    }
    ch[0][n + 1] = ch[0][n + 2] = ch[1][n + 1] = ch[1][n + 2] = '0';
    for (int i = n; i >= 1; i--) {
        for (int j = 0; j < 2; j++) {
            if (ch[j ^ 1][i] == '0') {  //对面没有脏东西,没啥好比较的
                dp[j][i] = dp[j][i + 1] + (ch[j][i + 1] - '0');
            }
            if (ch[j ^ 1][i] == '1') {  //对面有脏东西,先前拿走和不拿走都试一下(就是题目中的拿走)
                dp[j][i] = max(dp[j][i + 1] + (ch[j][i + 1] - '0'), 1 + (ch[j ^ 1][i + 1] - '0') + dp[j ^ 1][i + 2] + (ch[j ^ 1][i + 2] - '0'));
            }
        }
    }
    cout << dp[0][1];
    return 0;
}

参考代码2:

#include <bits/stdc++.h>
using namespace std;

int dp[200005][2][2];  //列 行
int dp2[200005][2];
string s[2];
int n;
int dpp(int p, int q, int r) {  // p是列,q是行
    int &ans = dp[p][q][r];     //对于一个处于(p,q)处的机器人,它可以向右清洁,也可以向下清洁。
    if (p == n) ans = 0;
    if (~ans) return dp[p][q][r] = ans;
    int id = s[q][p] - '0';
    ans = id + dpp(p + 1, q, 0);                        //向右清洁一定是可以的,如果下面有1需要清洁,这一步的含义就是,我们把下面的1拿掉了,解决冲突。
    if (s[1 - q][p] == '1' && !r)                       // r=1是特殊状态,意味着,对面一定是没有灰尘的所以不进行这步,哪怕对面确实有灰尘
        ans = max(id + 1 + dpp(p + 1, 1 - q, 1), ans);  //这一步的含义是把右边的1拿掉了,下面的1保留着,看看这样的情况是什么答案。
    return dp[p][q][r] = ans;
}
int dpp2(int p, int q) {  // p列 q行 dp是[列][行] s[行][列]
    int &ans = dp2[p][q];
    if (p >= n) ans = 0;
    if (~ans) return dp2[p][q] = ans;
    int id = s[q][p] - '0';
    ans = id + dpp2(p + 1, q);
    if (s[1 - q][p] == '1') {  //考虑p+2列的状态即可
        ans = max(id + 1 + (s[1 - q][p + 1] - '0') + dpp2(p + 2, 1 - q), ans);
    }
    return dp2[p][q] = ans;
}
int main() {
    memset(dp, -1, sizeof(dp));
    memset(dp2, -1, sizeof(dp2));
    scanf("%d", &n);
    cin >> s[0] >> s[1];
    s[0] += "00", s[1] += "00";
    printf("%d", dpp2(0, 0));
    return 0;
}

其中dpp和dpp2都是正确的,只是,两个不同的想法。即:对于冲突,我们怎么转移。Over。

————
喜欢请给我一个赞赞,谢谢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值