C++:2024/3/31 (Codeforces)

文章讲述了三道Codeforces题目,涉及构造有序数组、根据给定数组反推全排列以及计算多边形对角线形成三角形的数量,强调了观察问题本质、贪心策略和算法设计的应用。
摘要由CSDN通过智能技术生成

A. Farmer John's Challenge

原题链接:Problem - A - Codeforces

题目大意:

给你一个n,一个k。要求你首先判断能不能构造,能的话输出,不能的话输出-1。构造一个长度为n,cyclic shift后正好有k次是有序的。

题目做法:

cyclic shift有序的,只有两种情况要么数组不是全等的,只能有一次是有序的,其次就是怎么shift都是有序的。

所以k = 1的时候,一定可以,输出一个有序的即可。k = n的时候,长度为n的且值都相等的数组。其他情况就是不行。

AC代码:

#include<bits/stdc++.h>
#define pb(element) push_back(element)
#define fast ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//#define int long long
using namespace std;
const int mode = 1e9 + 7;
const int maxn = 2e5 + 10;
void solve(){
    int n, k;
    cin >> n >> k;
    if(k == 1){
        for(int i = 1; i <= n; i++){
            cout << i << ' ';
        }
        cout << '\n';
        return ;
    }
    if(n == k){
        for(int i = 1; i <= n; i++){
            cout << 1 << ' ';
        }
        cout << '\n';
        return ;
    }
    cout << "-1" << '\n';
}
signed main(){
    fast int casen = 1;
    cin >> casen;
    while(casen--) solve();
}

B. Bessie and MEX

原题链接:Problem - B - Codeforces

题目大意:

p是全排列数组,给你a数组,要你反向构造p数组,保证有解,a,p数组间关系一句话。

题目做法:

一定是有方法O(1)顺着判断下去的,找规律,找出这个方法。

注意,a中数组元素如果小于0,说明mex的值没有因为因为当下的p元素而增大,如果大于0,则mex的值因为加入当前p元素值后增加了,而且mex值的增加必定意味着,与mex的值对应相等的元素入队了,所以得出这两点就显然了。

AC代码:

#include<bits/stdc++.h>
#define pb(element) push_back(element)
#define fast ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//#define int long long
using namespace std;
const int mode = 1e9 + 7;
const int maxn = 2e5 + 10;
void solve(){
    int n, t, mex = 0;
    map<int,bool> isin;
    vector<int> res;
    cin >> n;
    for(int i = 0; i < n; i++){
        cin >> t;
        if(t < 0){
            res.pb(mex + abs(t));
        }
        else{
            res.pb(mex);
        }
        isin[res[res.size() - 1]] = 1;
        while(isin[mex] == 1){
            mex ++;
        }
    }
    for(int i = 0; i< res.size() ; i++){
        cout << res[i] << ' ';
    }
    cout << '\n';
}
signed main(){
    fast int casen = 1;
    cin >> casen;
    while(casen--) solve();
}

C1. Bessie's Birthday Cake (Easy Version)

原题链接:Problem - C1 - Codeforces

题目大意:

给你一个正n多边形,好像正不正没说,也许,不过不影响。x个点的点集合,要求再这x点里任选点作为对角线的起点和终点连线,连线与连线之间不交叉,问你围出来的最大三角形个数是多少。还有一个y的输入,不过C1不用,C1,y输入统一输入零。

题目做法:

我不知道是这个题目出的,还是我理解的太跳跃了,我觉得一开始就应该用选取的点描出一个多边形,产生的三角形就是整个多边形内部能产生最大的加上这个多边形和外部多边形形成的三角形,我觉得其实随便连只要不交叉的线都连满就是最大的,所有其实不太存在某种最大连法,这样做起来很奇怪,也许我的思维一步到位了,但是没有那种最优的感觉,只能说我这种思考方式比较好计算,很奇怪的一道题。最优解就是连满,连满就是最优解,那请问最优在哪里,就很奇怪。

事后ps:  多连线肯定能多增加,连满就是最优,那么最优连法又是啥,就,难道是我理解有问题,或者随便连满不一定是最优的吗,也不对吧,比如说你想,在集合里的点连成的一个最大多边形,其实是固定的,这个最大多边形内部的三角形个数是确定的,和最外围给的多边形形成的三角形也是确定的,如果连满,这个最大多边形的轮廓一定会有,内部其实随便怎么连都不影响其生成的三角形个数。所以把所有线都连满,无论怎么连,答案都是一样且最大的,所以不存在最优连法,都是最优连法,对对就是这样一种感觉,我感觉我讲清楚我的困惑了,不过怎么说呢,不影响做题,你可以认为我是闲的慌。。。。。。

注意4的特判以及头尾衔接,我是能踩的坑全踩一遍,我是天才。。

事后ps: 注意了头尾衔接就无需特判4,我是天才。。。

不过不挖这种坑放在C题确实有点不合适了。

AC代码:

#include<bits/stdc++.h>
#define pb(element) push_back(element)
#define fast ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//#define int long long
using namespace std;
const int mode = 1e9 + 7;
const int maxn = 2e5 + 10;
void solve(){
    int n, x, y, res = 0;
    cin >> n >> x >> y;
    int ar[x];
    for(int i = 0; i < x; i++){
        cin >> ar[i];
    }
    sort(ar, ar + x);
    for(int i = 1; i < x; i++){
        if(ar[i] == ar[i - 1] + 2){
            res++;
        }
    }
    if((ar[x - 1] + 2) % n == ar[0]) res++;
    if(n == 4){
        if(res != 0 || x >= 3){
            cout << 2 << '\n';
        }
        else{
            cout << 0 << '\n';
        }
        return ;
    }
    cout << res + x - 2 << '\n';
}
signed main(){
    fast int casen = 1;
    cin >> casen;
    while(casen--) solve();
}

C2. Bessie's Birthday Cake (Hard Version)

原题链接:Problem - C2 - Codeforces

题目大意:

比C1多的就这个y,你可以增加y个点,要求围出的三角形个数最大。

题目做法:

首先要认识到,有三种加点情况,加一个点新形成两个三角形,加一个点新形成一个三角形,和加一个点生成0个三角形,这里的生成指的是内部多边形和外部多边形两边括起来的生成,先填满形成两个的,再填满形成一个的,应该就完毕了。

事后ps:我觉得我一开始写得不是很具体,我重写解释一下,加一个点形成0个三角形不要去考虑,因为相当于没加,这种情况对应的是本来在集合种的点间距为1,2的情况。加一个点和外围围成的多两个三角形对应的情况是间距为4以上的偶数的情况,这种加点最后一个加点能多3个三角形,收益最高(和外围围成加2,内部多边形加一个点也即三角形加1,总加为3),加一个点和外围围成的多一个三角形对应的是间距为奇数的情况,以及填补前一情况补不满的情况,一个加点多2个三角形(内部多边形新加点多1个,加上和外围围成的加1个)。

形成两个的有是指最终点才形成两个的(和外围多边形形成的),总之这一种的先放,其他的收益不会有这个高,所以再第一个放不了了再说,贪心思路比较简单也比较明显,按间隔分类,贪心地放。

wa了1发才A的,不过错误也比较明显,发现的也比较容易,就是我一开始写的时候,做完第一个,就直接做第二个了,其实第一个剩下的也可以放到第二类里去的,多写了一个else(最后一个for循环种的else)把第一类的剩余情况放到第二类里就AC了。

事后ps:  如果还是无法理解,画图把原集合种点间距与加点的各种的情况列出来,再看看解释,不可能不懂了就。。。。。

AC代码:

#include<bits/stdc++.h>
#define pb(element) push_back(element)
#define fast ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//#define int long long
using namespace std;
const int mode = 1e9 + 7;
const int maxn = 2e5 + 10;
void solve(){
    int n, x, y, res = 0, total = 0;
    vector<int> g1, g2;
    cin >> n >> x >> y;
    int ar[x];
    for(int i = 0; i < x; i++) cin >> ar[i];
    sort(ar, ar + x);
    for(int i = 1; i < x; i++){
        if(ar[i] == ar[i - 1] + 2){
            res++;
        }
        if(ar[i] - ar[i - 1] <= 2) continue;
        if((ar[i] - ar[i - 1] - 4) % 2 == 0) g1.pb(((ar[i] - ar[i - 1]) - 4) / 2 + 1);
        else total += (ar[i] - ar[i - 1] - 1) / 2;
    }
    if(n - ar[x - 1] + ar[0] > 2){
        //cout << n - ar[x - 1] + ar[0] - 4 << '\n';
        if((n - ar[x - 1] + ar[0] - 4) % 2 == 0) g1.pb(((n - ar[x - 1] + ar[0]) - 4) / 2 + 1);
        else total += (n - ar[x - 1] + ar[0] - 1) / 2;
    }
    if((ar[x - 1] + 2) % n == ar[0]) res++;
    res = res + x - 2;
    sort(g1.begin(), g1.end());
    for(int i = 0; i < g1.size(); i++){
        if(y >= g1[i]){
            y -= g1[i];
            res += (g1[i] - 1) * 2 + 3;
        }
        else{
            //cout << g1[i] << '\n';
            total += g1[i] - 1;
            //cout << total << '\n';
        }
    }
    res += min(y, total) * 2;
    cout << res << '\n';
}
signed main(){
    fast int casen = 1;
    cin >> casen;
    while(casen--) solve();
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值