牛客小白赛 Round 95 D-F

前言:

A - 相遇

思路:

  • 枚举即可

以下是代码部分

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

int main()
{
    int a, b;
    cin >> a >> b;
    if(a == b) cout << "p";
    else if(a == 1 && b == 2) cout << 'a';
    else if(a == 2 && b == 3) cout << "a";
    else if(a == 3 && b == 1) cout << 'a';
    else cout << 'b';

    return 0;
}

B-宝石

思路:

  • 总共就两种最短路的方法
    • 只通过路径长度为A的路拿取宝石,路程总和为11*A
    • 通过1条长度为A,5条长度为B的途径拿取宝石 路程总和为:A+5*B
  • 选取路程最短的路,输出。

以下是代码部分

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

int main()
{
    int a, b;
    cin >> a >> b;
    cout << min(11 * a, 5 * b + a);

    return 0;
}

C - 相助

思路:

  • 因为此题a[i]只有01两种元素,所以分类讨论即可
    1. 当开头元素等于末尾元素时: 操作数为1
    2. 当元素数量n=1时:不可操作,输出:-1
    3. 当第i位元素和开头元素相等,并且i+1位元素和结尾元素相等时,操作数为:2
    4. 都不符合的情况,为不可操作,输出:-1
      • 比如:0100001符合情况4

以下是代码部分

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

const int N = 5e5 + 10;
int a[N];

int main()
{
    int n;
    cin >> n;
    for(int i = 1; i <= n; i ++)
        cin >> a[i];
	//取头位置元素, 取尾位置元素
    int head = a[1], tail = a[n];
	//情况2
    if(n == 1)
        return cout << "-1\n", 0;
	//情况1
    if(head == tail)
        return cout << "1\n", 0;
	//遍历判断情况3是否成立
    for(int i = 2; i < n - 1; i ++)
        if(head == a[i] && tail == a[i + 1])
        {
            cout << "2\n";
            return 0;
        }
    //情况4
    cout << "-1\n";

    return 0;
}

D - 异或炸弹(easy)

思路:

  • 前置知识:差分数组
    • 在每次投放炸弹后,差分炸弹在每一行可以达到的范围。
  • 最后遍历得出为1的个数

差分数组简要介绍:

  • 在数组a[]
  • 如果要使区间l-r的每个元素+1
  • 可以使a[l] + 1使a[r + 1] - 1
  • 在处理后,通过前缀和的操作,就可以使区间a[l] - a[r]的每个元素+1并且其他区间不受影响。
    在这里插以下是代码部分,代码参考来源——FriedChicken
#include<bits/stdc++.h>
using namespace std;

int mp[6010][6010];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    int x, y, r;
    while(m --)
    {
    	//输入炸弹的坐标以爆炸范围
        cin >> x >> y >> r;
        //遍历x方向上的曼哈顿范围
        for(int i = x - r; i <= x + r; i ++)
        {
        	//注意不可越界
            if(i < 1 || i > n) continue;
            //求每一行的曼哈顿范围
            int l = r - abs(x - i);
            //差分处理
            //注意不可以越界
            mp[i][max(1, y - l)] ^= 1;
            mp[i][min(n + 1, y + l + 1)] ^= 1;
        }
    }
    int ans = 0;
    //遍历地图,计数
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= n; j ++)
        {
        	//前缀预处理
            mp[i][j] ^= mp[i][j - 1];
            //如果为mp[i][j]上的位置为1,则ans ++
            ans += mp[i][j] & 1;
        }

    cout << ans << '\n';

    return 0;
}

E - 相依

思路:

  • 动态规划
  • dp[i]:dp[消除1-i项]所需最少操作次数
  • mn[i]:mn[值的大小为i的元素]被消除所需的最小次数
  • 可得递推公式:
    • dp[i] = mn[a[i]] + 1消除1-i的元素,为消除a[i]的操作数 +1(因为之前mn[a[i]]是未匹配过的)
    • mn[a[i]] = min(mn[a[i]], dp[i - 1])在第i位的a[i]未匹配时,mn[a[i]]的状态转移方式

以下是代码部分,代码参考来源——FriedChicken

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

const int N = 5e5 + 10;
int a[N], dp[N], mn[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    // 初始化为无穷
    for(int i = 0; i < N; i ++) mn[i] = 1e9;
	//输入
    for(int i = 1; i <= n; i ++) cin >> a[i];
	//递推公式
    for(int i = 1; i <= n; i ++)
    {
        dp[i] = mn[a[i]] + 1;
        mn[a[i]] = min(mn[a[i]], dp[i - 1]);
    }
    //如果无法操作
    if(dp[n] >= 1e6) cout << "-1\n";
    //可以操作的时候,输出
    else cout << dp[n] << '\n';

    return 0;
}

F - 异或炸弹(hard)

前置知识:

思路:

  • 通过曼哈顿距离转切比雪夫距离,来方便二维前缀和,二维差分的处理
    • 注意,本题通过数组来模拟坐标,所以要对点进行Y轴上的偏移,防止为负数,导致越界
  • 处理好后,还要切比雪夫距离转曼哈顿距离,把多算的点排除掉

曼哈顿距离转切比雪夫 ( x + y , x − y ) (x+y, x-y) (x+y,xy)
切比雪夫转曼哈顿距离 ( x + y / 2 , x − y / 2 ) (x+y/2, x-y/2) (x+y/2,xy/2)
以下是代码部分

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

const int N = 6e3 + 10;
int f[N][N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= m; i ++)
    {
        int x, y, r;
        cin >> x >> y >> r;
        // 曼哈顿距离转换成切比雪夫距离
        //      +3000 为防止出现负数,放于坐标系f[][]中
        int nx = x + y, ny = x - y + 3000;
        //转换成切比雪夫距离后,便于差分
        //二维差分
        f[max(1, nx - r)][max(1, ny - r)] ^= 1;
        f[min(6000, nx + r + 1)][max(1, ny-r)] ^= 1;
        f[max(1, nx-r)][min(6000, ny + r + 1)] ^= 1;
        f[min(6000, nx + r + 1)][min(6000, ny + r + 1)] ^= 1;
    }
    int ans = 0;
    for(int i = 1; i <= 6000; i ++)
        for(int j = 1; j <= 6000; j ++)
        {
        	//二维前缀和
            f[i][j] = f[i-1][j]^f[i-1][j-1]^f[i][j-1]^f[i][j];
            //曼哈顿距离转换成切比雪夫距离
            int x = (i + j - 3000) / 2, y = (i - j + 3000) / 2;
            //判断值是否为1,(i+j)和(i-j)是否是偶数,   是否越界
            if(f[i][j] && (i&1) == (j&1) && x > 0 && x <= n && y > 0 && y <= n)
                ans ++;
        }
    cout << ans << '\n';

    return 0;
}
  • 32
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值