【Codeforces】Codeforces Round #787 (Div. 3) A-E

A. Food for Animals

第一题…

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
typedef pair<int, int> pii;

vector<int>v(N);

void solve() {
    ll a, b, c, x, y;
    cin>>a>>b>>c>>x>>y;
    if(min(a - x, 0ll) + min(b - y, 0ll) + c >= 0)
        cout<<"YES"<<endl;
    else
        cout<<"NO"<<endl;
}

int main()
{
    ios::sync_with_stdio(false);
    int t = 1;
    cin>>t;
    while (t--) {
        solve();
    }
    return 0;
}
B. Make It Increasing

题意描述:可以对数组中的任意元素除2(向下取整)。问:至少多少步,可以使数组严格递增。如果不可能严格递增,输出-1。

题意分析:从后往前(倒数第二个元素开始),如果大于等于下标加一的元素则进行除2,如果当前元素已经为0且仍然大于等于后一个元素则不可能。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
typedef pair<int, int> pii;

vector<int>v(N);

void solve() {
    int n, ok = 1, ans = 0;
    cin>>n;
    vector<int> v(n);
    for(int i=0;i<n;i++) {
        cin>>v[i];
    }

    for(int i=n-2;i>=0;i--) {
        while(v[i] == 0 && v[i] >= v[i + 1]) { // v[i] == 0时可以退出调整
            v[i] /= 2; // 调整操作
            ans++;
        }
        if(v[i] >= v[i + 1]) ok = 0; // 特判不可能情况
    }

    if(!ok) 
        cout<<-1<<endl;
    else
        cout<<ans<<endl;
}

int main()
{
    ios::sync_with_stdio(false);
    int t = 1;
    cin>>t;
    while (t--) {
        solve();
    }
    return 0;
}
C. Detective Task

题意描述:一开始房间有一幅画,最后房间画丢了。画主人询问每一个参观者,得到三种回答:0进入前画已经没了、1进入前画还在和?记不得了。其中,除小偷外每个人都说真话,小偷畅所欲言。问可能偷画的有几个人?

题意分析:首先,第一个说0和最后一个说1的人一定撒谎了,输出1即可。其次,第一个说0的人一定是有问题的,无论他是不是小偷(因为普通观赏着不能撒谎),此时左边最靠近他的说1的人和他是有矛盾的,或者之前没人说1则从头算起都有可能是小偷。最后,没人说零的话,最后一个说1的人及之后都有可能是小偷,在这种情况下连说1的都没有,则全部都可疑。

针对第三种情况:最后一个说1的人及之后都有可能是小偷。因为此情况下没人说0,则之前的人要么说’1’要么说’?',假设之前画已经被偷了则当前人说谎了,当前画没被偷则当前人也是最后一个接触者,因此撒谎者只有可能是说1的人及之后。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
typedef pair<int, int> pii;

void solve() {
    string s;
    cin>>s;
    int len = s.length(), ans = 0, ind = 0;
    for(int i=0;i<len;i++) {
        if(s[i] == '0') {
            ans = i - ind + 1; // ans存储第二种情况答案
            break;
        }
        if(s[i] == '1') ind = i; // 寻找最靠近'0'的'1',如果没有出现'0'则会标记最后一个'1'
    }
    
    if(s[0] == '0' || s[len - 1] == '1') //第一种情况
        cout<<1<<endl;
    else if(ans) // 第二种情况
        cout<<ans<<endl;
    else // 最后
        cout<<len - ind<<endl;
}

int main()
{
    ios::sync_with_stdio(false);
    int t = 1;
    cin>>t;
    while (t--) {
        solve();
    }
    return 0;
}
D. Vertical Paths

在这里插入图片描述
题意描述:将一颗数按照自顶向下的方式拆分,要求拆分路径无重合节点不重复,且拆分数最少。

题意分析:题目中拆分数最少,其实可以转化为一个叶子节点一条路径,没有比这更少的拆分了。

bfs嵌套dfs做法

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
typedef pair<int, int> pii;

int n, rt;
queue<int>q;

vector<int>tree[N];
vector<vector<int>>ans; //存储答案的数组

void dfs(int x)
{
    // 注意从1开始,因为对0号孩子进行dfs
    // 而0号孩子的兄弟是要作为子树的根节点的
    ans.back().push_back(x);
    for(int i=1;i<tree[x].size();i++) { // 兄弟节点加入队列
        q.push(tree[x][i]);
    }
    if(tree[x].size())
        dfs(tree[x][0]);
}

void solve() {
    cin>>n;
    ans.clear();
    for(int i = 0; i <= n; i++) tree[i].clear();
    vector<int> a(n + 1);
    for(int i=1;i<=n;i++) {
        cin>>a[i];
        if(a[i] == i) rt = i;
        else { // 建树
            tree[a[i]].push_back(i); //当前元素是i的父亲
        }
    }

    q.push(rt);
    while(!q.empty())
    {
        rt = q.front();
        q.pop();
        ans.push_back(vector<int>());
        dfs(rt);
    }

    cout<<ans.size()<<endl;
    for(auto v : ans) {
        cout<<v.size()<<endl;
        for(auto item : v) {
            cout<<item<<" ";
        }
        cout<<endl;
    }
}

int main()
{
    ios::sync_with_stdio(false);
    int t = 1;
    cin>>t;
    while (t--) {
        solve();
    }
    return 0;
}

从叶子节点入手做法

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
typedef pair<int, int> pii;

int n, rt;
int vis[N];

void solve() {
    cin>>n;
    vector<int> a(n + 1), lef;
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++) {
        cin>>a[i];
        if(a[i] == i) rt = i;
        vis[a[i]] = 1;
    }

    for(int i=1;i<=n;i++) {// 确定叶子节点
        if(!vis[i]) lef.push_back(i);
        vis[i] = 0;
    }
    if(n == 1) lef.push_back(1);

    vector<vector<int>> ans;
    for(int i=0;i<lef.size();i++) {
        int now = lef[i];
        vector<int> v;
        while(now && !vis[now]) {
            v.push_back(now);
            vis[now] = 1;
            now = a[now];
        }
        if(v.size()) ans.push_back(v);
    }

    cout<<ans.size()<<endl;
    for(auto v : ans) {
        cout<<v.size()<<endl;
        for(int i = v.size() - 1; i >= 0; i--) {
            cout<<v[i]<<" ";
        }
        cout<<endl;
    }
}

int main()
{
    ios::sync_with_stdio(false);
    int t = 1;
    cin>>t;
    while (t--) {
        solve();
    }
    return 0;
}
E. Replace With the Previous, Minimize

题意描述:对一个只包含小写字母的字符串进行操作,每次可以让同一种字符减一。最终的到的字典序最小的串是?

题意分析:从第一个字符开始贪心(自减)。注意:大字符变为较小的字符时,它中间遇到的所有字符可以同之后串中出现的同一字符一起减;另外,一字符自减过程中的字符在之前被标记过(可以同之前同一字符一起自减),则不用自减。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
typedef pair<int, int> pii;

void solve() {
    int n, k;
    char vis[130] = {0}, ch[130] = {0};
    string s;
    cin>>n>>k>>s;
    // 递减并标记所遇到的字符
    for(auto c : s) while(c > 'a' && !vis[c] && k > 0) vis[c] = 1, c --, k --;

    for(int i = 'a'; i <= 'z'; i++) {
        for(int j = i; j >= 'a'; j--) { // 查看每个小写字母最小可以减为什么字母
            ch[i] = j;
            if(!vis[j]) break; // 退出位置很关键, 因为vis不标记最后退出的字符
        }
    }
    for(auto &c : s) c = ch[c];

    cout<<s<<endl;
}

int main()
{
    ios::sync_with_stdio(false);
    int t = 1;
    cin>>t;
    while (t--) {
        solve();
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sophon、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值