Codeforces Round #674 (Div. 3)

C#

问题描述#

初始状态给你一个 1,有两种操作

  1. 给其中一个数字增加 1
  2. 复制其中一个数字并添加到该序列中

问你最少要几次操作,才能得到数字 n

题解#

tag:贪心,枚举

对于两个操作来说,肯定是 先 +1 再复制,得到的数字更大,(因为这个+1 操作被复制成了两份),

否则我们可以替换两个操作之间的顺序,得到更大的值,来逼近目标 n

假定我们一直使用操作一,得到的序列总和为 x

所以就可以枚举 1n ,对于每个x 计算一次答案,取所有结果的min 即可

1x 需要x1 次操作 ,从 x 逼近到 n 需要(nx)x 次操作,向上逼近是为了进行余下的第一种操作,比如 11,5 这些

代码#

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>

using namespace std;
#define endl '\n' 
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
typedef pair<int,int> PII;

const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
int main() {
#ifndef ONLINE_JUDGE
    freopen("D:/scode/in.txt","r",stdin);
    //freopen("D:/scode/out.txt","w",stdout);
#endif
    IO;
    int _;
    cin >> _;
    while(_--) {
        int n,ans = INF;
        cin >> n;
        for(int i = 1;i <= n / i; ++i) {
            // 向上取整的简单写法 (n - a + (b - 1)) / b;
            ans = min(ans,i - 1 + (n - i + (i - 1)) / i);
        }
        cout << ans << endl;
    }
    return 0;
}

D#

问题描述#

给你一个序列,问你其中有多少个子段的和为 0

题解#

tag:前缀和

考虑前缀和,从左到右一直累加到 presum 中,如果有两次的presum 相等,那么中间肯定有一段 所有值相加为 0 的子段

因为,一直累加presum ,不可能从lr presum 不变,(序列中只有正负数,没有 0),如果不变,只能是+0

那么我们把重复出现过的presummap 记录一下就行,注意初始化 map[0]=1 ,因为重复出现了 presum=0 也算

代码#

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>

using namespace std;
#define endl '\n' 
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
typedef pair<int,int> PII;

const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
int main() {
#ifndef ONLINE_JUDGE
    freopen("D:/scode/in.txt","r",stdin);
    //freopen("D:/scode/out.txt","w",stdout);
#endif
    IO;
    int n,x,ans = 0;
    map<LL,int> m;
    LL sum = 0;
    cin >> n;
    m[0] ++;
    for(int i = 0;i < n; ++i) {
        cin >> x;
        sum += x;
        if(m[sum] > 0) {
            ans ++;
            sum = x;
            // 清空 map
            m.clear();
            m[0] ++;
        }
        m[sum] ++;
    }
    cout << ans << endl;
    return 0;
}

E#

问题描述#

A 和 B 两个人玩剪刀石头布,一共有 n

已知 A 出了 a1 次石头 a2 次剪刀 a3 次布,a1+a2+a3=n

B同样出了 b1,b2,b3 含义一样 b1+b2+b3=n

问 A 最小能赢的次数,A最大能赢的次数

题解#

tag:贪心,博弈

A最大能赢的次数就是,用 石头去干剪刀,剪刀去干布,布去干石头,即可,取三者的min 值相加

min(a1,b2)+min(a2,b3)+min(a3,b1)

A最小能赢的次数就是,用石头去干布,然后用剩下的次数去干石头,剩下两个同理

或者计算,总数 - (平局 + 输的最多的次数)= 赢的最少的次数

(平局 + 输的最多的次数) = min(a1,b1+b3) 去最小即可,因为要计算 a1 能被分多少到 b1 (平局)和 b3 (输局)

代码#

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>

using namespace std;
#define endl '\n' 
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
typedef pair<int,int> PII;

const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
int n,a[3],b[3];
int main() {
#ifndef ONLINE_JUDGE
    freopen("D:/scode/in.txt","r",stdin);
    //freopen("D:/scode/out.txt","w",stdout);
#endif
    IO;
    cin >> n;
    cin >> a[0] >> a[1] >> a[2];
    cin >> b[0] >> b[1] >> b[2];
    int Amx,Ami = 0;
    Amx = min(a[0],b[1]) + min(a[1],b[2]) + min(a[2],b[0]);
    Ami = n - (min(a[0],b[0] + b[2]) + min(a[1],b[1] + b[0]) + min(a[2],b[2] + b[1]));
    cout << Ami << ' ' << Amx << endl;
    return 0;
}

F#

问题描述#

给你一个长度为n 的字符串,仅包含a,b,c,? 这四种字符,其中? 可以变成任意一种其他字符 a,b,c ,求所有字符串中子序列abc 的个数

题解#

tag:DP

f[i][0] 表示前 i 个字符中有多少种序列情况

f[i][1] 表示前i 个字符中有多少个 a

f[i][2] 表示前i 个字符中有多少个ab

f[i][3] 表示前i 个字符中有多少个abc

如果第i 位为a ,则 f[i][1]=f[i1][1]+f[i1][0]

其他同理

注意,每一个问号都有三种情况,所以一直累乘

代码是优化成一维的

代码#

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>

using namespace std;
#define endl '\n' 
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
typedef pair<int,int> PII;

const int N = 2e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
LL f[4];
// f0 ?
// f1 a
// f2 ab
// f3 abc
int main() {
#ifndef ONLINE_JUDGE
    freopen("D:/scode/in.txt","r",stdin);
    //freopen("D:/scode/out.txt","w",stdout);
#endif
    IO;
    int n;
    char ch;
    cin >> n;
    f[0] = 1;
    while(n --) {
        cin >> ch;
        if(ch == 'a') {
            // 前面的 a 和 ? (也能变成 a )
            f[1] = (f[1] + f[0]) % mod;
        }
        else if(ch == 'b') {
            // 前面的 ab 和 (前面的 a + 当前的 b) == ab 
            f[2] = (f[2] + f[1]) % mod;
        }
        else if(ch == 'c') {
            // 前面的 abc 和 (前面的 ab + 当前的 c) == abc
            f[3] = (f[3] + f[2]) % mod;
        }
        else {
            // 碰到了 ?
            // 从后往前,因为要利用上一层的值,参考01背包的滚动优化
            for(int i = 3;i >= 1;--i) {
                // 一个 ? 能把当前序列分成三种不同的情况
                // 前面都一样是 f[i] 第 i 个是问号,可能会是 f[i]'a',f[i]'b',f[i]'c'
                // 所以 前面的f[i] * 3 ,然后加上 当前这个问号变成 f[i - 1]  
                f[i] = (f[i] * 3 + f[i - 1]) % mod;
            }
            // 每个 ? 对应一个 * 3
            f[0] = f[0] * 3 % mod;
        }
    }
    cout << f[3] << endl;
    return 0;
}
posted @   南风--  阅读(95)  评论(0编辑  收藏  举报
编辑推荐:
· 理解Rust引用及其生命周期标识(下)
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
阅读排行:
· C# 13 中的新增功能实操
· Ollama本地部署大模型总结
· 2025成都.NET开发者Connect圆满结束
· langchain0.3教程:从0到1打造一个智能聊天机器人
· 用一种新的分类方法梳理设计模式的脉络
点击右上角即可分享
微信分享提示
主题色彩