Educational Codeforces Round 92 (Rated for Div. 2) ABC--白话题解

#include<queue>
#include<map>
#include<algorithm>
#include<cmath>
#include<unordered_map>
#include<time.h>
#define F(i,a,b) for(int i=a;i<=b;i++)
#define FD(i,b,a) for (int i=b;i>=a;i--)
#define ll long long
#define db double
using namespace std;

A:
给L,R 要求两个数ans1、ans2,使得L <=ans1、ans2、LCM(ans1,ans2) <=R (ans1 != ans2)

(不妨认定ans1<ans2)
由于ans1 * ans2 == LCM(ans1,ans2) * GCD(ans1,ans2)
所以我们要让LCM尽量小,那么GCD就要尽量大
GCD最大是ans1
此时LCM为ans2
为了让此时的ans1小一些 ans1就取L 而为了让ans2小一些 ans2只能取2*ans1

    int T;
    cin >> T;
    while (T--) {
        ll L, R; ll ans1, ans2;
        cin >> L >> R;
        if (L * 2 > R) {
            cout << "-1 -1\n";
        }
        else {
            cout << L << ' ' << L * 2 << endl;
        }
    }

B:
从a1 开始 (ans 初始为 a1) 进行k次如下操作中的一种:

  1. 从 ai 走到 ai+1 并把 ai+1 加到ans中
  2. 从 ai 走到 ai-1 并把 ai-1 加到ans中 (前提是上一步操作不是操作 2 ,也就是不能连续返回走)

注意到数据中限制了往回走的次数不多于5次 而且数组长度也不过1w 是个很显然的动规框架
因为操作2不允许连续做的限制,所以状态的表示要多一维表示是否能够往回走
用 f[i][j][k] 表示: 取到 i 处,用了 j 次返回 ,k = 0 表示可以从此处返回 k = 1 表示不可以
转移方程2行即可:

            f[i][j][0] = max(f[i - 1][j][0] + a[i], f[i - 1][j][1] + a[i]);
			// 可以往回走 只能取到前面一格时往后走一格
            f[i][j][1] = f[i + 1][j - 1][0] + a[i];
            // 不可以往回走 只能是从后面一格的可以往回走的状态下转移过来
int main()
{
    int T;
    cin >> T;
    while (T--) {
        int n, k, z;
        cin >> n >> k >> z;
        F(i, 1, n+1) {
            F(j, 1, z) {
                f[i][j][0] = 0;
                f[i][j][1] = 0;
            }
        }//预处理

        F(i, 1, n) {
            scanf("%lld", a+i);
        }
			//DP主体
        F(i, 1, n) f[i][0][0] = f[i - 1][0][0] + a[i];
        F(j, 1, z) {
            F(i, 1, n) {
                f[i][j][0] = max(f[i - 1][j][0] + a[i], f[i - 1][j][1] + a[i]);

                f[i][j][1] = f[i + 1][j - 1][0] + a[i];
                
            }
        }

        ll ans = f[k+1][0][0]; //最后从可能答案中取的时候 也有一些注意点
        F(i, 1, z) {
            if (k + 1 - (2 * i) > 0) {
                if (f[k + 1 - (2 * i)][i][0] > ans) ans = f[k + 1 - (2 * i)][i][0];
                if (f[k + 1 - (2 * i)][i][1] > ans) ans = f[k + 1 - (2 * i)][i][1];
            }
        }
        cout << ans << endl;


    }

    return 0;
}

C:
对于一个字符串S 如果:
将它的首位移动到末尾得到的S1
将它的末位移动到首位前得到的S2
S1==S2 那么说明这个串S是好串
现在问给一个串去掉最少的字符使之变成好串 输出去除数

首先要分析得到,当一个串是好串的时候 具有一种传递性
使得要有1 3 5 7…位都是一样的0 2 4 6…位都是一样的
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200731213731458.png在这里插入图片描述

(如图 先看后两行的传递 他们实际代表的位置在第一行)
所以最后的好串只有2个以下的不同数字构成
所以暴力枚举这两个数字字符是哪两个,然后在原串中找符合这两个字符一个隔着一个的排列的非连续子串 最后比较的最大
有个小坑点,如果两个数字字符不一样 那么最后的答案一定是偶数 是奇数的话-1
而如果字符一样 那么最后答案就不需要管奇偶性


char s[200020] = {};
int mp[10] = {};
int main()
{
    int T;
    cin >> T;
    while (T--) {
        memset(s, 0, sizeof(s));

        scanf("%s", s + 1);

        int len = strlen(s+1);

        memset(mp, 0, sizeof(mp));
        int ans = 0;
        F(i, 0, 9) {
            F(j, 0, 9) {		//爆搜这俩字符
                int len2 = 0;
                int preIsI = 0;
                int sta = 0;
                F(k, 1, len) { //看哪一个字符先出现
                    if (s[k] == '0' + i) {
                        len2++;
                        preIsI = 1;
                        sta = k + 1;
                        break;
                    }
                    if (s[k] == '0' + j) {
                        len2++;
                        preIsI = 0;
                        sta = k + 1;
                        break;
                    }
                }

                F(k, sta, len) {	//找一个隔着一个的最长非连续串
                    if (s[k] == i + '0' && !preIsI) {
                        preIsI = 1;
                        len2++;
                    }
                    else if (s[k] == j + '0' && preIsI) {
                        preIsI = 0;
                        len2++;
                    }
                }
                
                if (i != j) {  //小坑点
                    if (len2 % 2 == 1) {
                        len2--;
                    }
                }
                if (len2 > ans) {
                    ans = len2;
                }

            }
        }

        cout << len-ans << endl;
    }

    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值