我无法选择命运|

fufufuf

园龄:1个月粉丝:0关注:1

CODEFORCE 998 DIV3

拖了好久,放假归来,终于可以把拖了很久的博客给写了。这场比赛都过去好长一段时间了,看来脱了很久了,所以现在补一下,算是一个补题的动力吧。

第一个题,属于简单题,直接暴力验证就可以了,当时因为python有一些比较友好的函数,避免了一些流程上的麻烦,所以我就直接使用python就可以解决了,一般会使用pypy,这样子比较快速,当然,pypy的栈空间不是特别大,所以对于高递归的不好处理,但是这道题不需要这么复杂,下面就是AC代码。

n=int(input())
from collections import Counter
for i in range(n):
    a1,a2,a4,a5=map(int,input().split())
    list1=[a1+a2,a4-a2,a5-a4]
    a=Counter(list1).most_common()
    print(a[0][1])

第二题,这个题有一点像一个归并排序的合并,但是我们在做这个题目的时候,不可以去直接像归并排序那样处理,但是这里我们有sort,只要把每一个牛有的牌给按照顺序排序就可以了,由于每一个只有2000个,所以排序的时间也是充足的,在这里,由于题目说了保障每一个数字都是依次递增的,所以说在这里只要按照顺序去搜索就可以了,找到了之后,把当前数字给弹出来,之后加入一个当前的位置信息到总和数组里面,最后对于这个数组遍历就可以了,检查每一个取模之后的结果是否相等就可以了。注意的是在弹出数字的时候一定要检查数组是否为空,所以不然会WA(因为这个浪费了一个小时+4发罚时)下面是AC的代码。

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


inline void sol(){
    vector<queue<int>> list1;
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++){
        vector<int> v;
        for(int j=1;j<=m;j++){
            int num;scanf("%d",&num);
            v.push_back(num);
        }
        sort(v.begin(),v.end());
        queue<int> q;
        for(int j:v){
            q.push(j);
        }
        list1.push_back(q);
    }

    int num3=0;
    vector<int> num2;
        while(num3<n*m){
        for(int i=0;i<n;i++){
            if(list1[i].size()!=0 && list1[i].front()==num3){
                list1[i].pop();
                num2.push_back(i+1);
                num3++;
            }
        }
    }
    bool flag=true;
    for(int i=0;i<num2.size();i++){
        if (num2[i]!=num2[i%n]){
            flag=false;
            break;
        }
    }

    if (flag){
        for(int i=0;i<n;i++){
            cout<<num2[i]<<" ";
        }
        cout<<endl;
    }
    else{
        cout<<"-1"<<endl;
    }

}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        sol();
    }
}

第三题,这个是一个虚假的博弈论的题目。里面说一个人想让数字选择争取a+b=k 另一个要求 a+b!=k,但是在这里,如果仔细观察样例,就会发现,其实输出的结果就是有多少对数字可以使a+b=k,不存在什么博弈的情况,所以直接排序后双指针结束了,下面是AC代码

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

inline void sol(){
    int n,k;scanf("%d %d",&n,&k);
    vector<int> v;
    for(int i=0;i<n;i++){
        int x;scanf("%d",&x);
        v.push_back(x);
    }

    sort(v.begin(),v.end());

    int l=0;int r=n-1;
    int res=0;
    while(l<r){
        if(v[l]+v[r]<k){
            l++;
        }
        else if (v[l]+v[r]>k){
            r--;
        }
        else{
            l++;
            r--;
            res++;
        }
    }
    cout<<res<<endl;
}

int main(){
    int T;scanf("%d",&T);
    while(T--){
        sol();
    }
}

第四题,一开始看到觉得头大,后来才发现原来两个数字必须是相互挨着的,这时题目就变得简单太多了,于是就直接for循环平推过去,然后再去检查最后的结果,然后就可以AC了,C,D两题整体难度不大,但是B题浪费太多时间,算是比赛的遗憾。如下是AC的代码

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

inline void sol(){
    int n;scanf("%d",&n);
    vector<int> v;
    for(int i=0;i<n;i++){
        int a;scanf("%d",&a);
        v.push_back(a);
    }

    for(int i=0;i<n-1;i++){
        int num=min(v[i],v[i+1]);
        v[i]-=num;
        v[i+1]-=num;
    }
    bool flag=true;
    for(int i=1;i<n;i++){
        if(v[i]>=v[i-1]){
            continue;
        }
        else{
            flag=false;
            break;
        }
    }
    if(flag){
        cout<<"YES"<<endl;
    }
    else{
        cout<<"NO"<<endl;
    }
}

int main(){
    int T;scanf("%d",&T);
    while(T--){
        sol();
    }
}

然后是第五题,这后面的题没有时间做了,所以就按照答案来讲。
首先,题目会输入n,m1,m2分别表示n个点,F图里面有m1个边,G图里面有m2个边,在这里,题目要求的是对图F进行删除边和增加边两种操作,然后使F存在一条边当且仅当G中存在一个相同的边。
那么首先,我们需要排除掉所有G中不存在,但是F存在的边,在找到这样子的边之后,移除这一条边,如此去掉多余的边之后,此时就是要通过添加边来保证连通性是一样的,即联通分量一样,那么在这里,由于要保证联通分量一样,那么只要在操作两个联通分量的数量只差次就可以了,因为在这里,F图所拥有的边,一定是G已经拥有的,所有此时只要去增加即可。
下面是AC代码。

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

void Gdfs(int v,vector<vector<int>> &sl,vector<int> &col,int c){
    col[v]=c;//将v节点染色为c
    for(int u:sl[v]){//遍历c的每一个邻接节点u
        if(col[u]==0){//若是u没有被染色,那么就对u染色
            Gdfs(u,sl,col,c);
        }
    }
}

//old col表示旧的染色信息,这个信息是用来做参考的
int Fdfs(int v,vector<vector<int>> &sl,vector<int> &col,vector<int> 
&old_col,int c){
    col[v]=c;//将v染色味c
    int res=0;
    for(int u:sl[v]){
        if(col[u]==0){
            if (old_col[u]!=c){//如果u并没有被染色c,那么就让res++
                res++;
            }
            else{//否则就继续往下搜索所有的节点来染色
                res+=Fdfs(u,sl,col,old_col,c);
            }
        }
    }
    return res;
}

void read_con_list(vector<vector<int>> &sl,int m){
    //构建邻接表
    for(int i=0;i<m;i++){
        int u,v;
        scanf("%d %d",&u,&v);
        //保证是从0开始
        sl[--u].emplace_back(--v);
        sl[v].emplace_back(u);
    }
}

void sol(){
    int n,mf,mg;
    scanf("%d %d %d",&n,&mf,&mg);
    vector<vector<int>> fsl(n),gsl(n);
    read_con_list(fsl,mf);
    read_con_list(gsl,mg);

    vector<int> fcol(n),gcol(n);

    int ans=0;//记录最后的结果
    for(int i=0;i<n;i++){ //遍历所有的节点,对其进行染色。
        if(gcol[i]==0){//0表示未被染色
            Gdfs(i,gsl,gcol,i+1);//i+1表示被染上i+1颜色
        }
        if(fcol[i]==0){
            //f中当前节点未被染色
            ans+=Fdfs(i,fsl,fcol,gcol,gcol[i]);//以g为参考的时候,如果两个点连接的联通分量不同,那么会为ans
            if (gcol[i]<i+1){//若是当前染色是和他不一样,表示需要怎家一下边
                ans++;
            }
        }
    }
    cout<<ans<<endl;
}


int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        sol();
    }
    return 0;
}

然后是G题,F题没有C++的代码,所以就不想补了,直接看G题
G题是首先给了两个数组,然后通过对a[i],b[j]和a[j],b[i]交换,通过这样子的方式实现a数组和b数组两个数组都是升序的。题目要回答这两个数组是否可以实现这样子的情况。

在这里,首先对于每一个数对(a[i],b[i])来说,按照min(a[i],b[i])的顺序完成排序,
关于按照min(a[i],b[i])来排序的必要性证明,在这里,使用反证法来证明,首先假设存在一个数对(a[i],b[i]),满足a[i]<b[i],并且a[i]小于前一个数对的最小元素。
在最终的排序中,数对是有序排序的,若是要把数对(a[i],b[i])放在钱一个数对之后,由于a[i]会小于前面数对中的元素,影响到整体升序的原则,所以必须按照min(a[i],b[i])来升序排序。

接下来通过判断是否可以通过翻转数对来实现排序,这里,通过使用动态规划来解决问题。
这里dp的设定如下,有4种情况
1.若是可以使用偶数次翻转将第i个数对(包括第i个数对)及其之前的数对排序,且第i个数对不翻转,那么dp[1][i]=1;
2.若是可以使用偶数次翻转将第i个数对(包括第i个数对)及其之前的数对排序,且第i个数对翻转,那么dp[2][i]=1;
3.若是可以使用奇数次翻转将第i个数对(包括第i个数对)及其之前的数对排序,且第i个数对不翻转,那么dp[2][i]=1;
4.若是可以使用奇数次翻转将第i个数对(包括第i个数对)及其之前的数对排序,且第i个数对翻转,那么dp[3][i]=1;

这里的翻转是指将(a,b)变换成为(b,a)的意思。
在这里对于状态转移的方程,则是要讨论两种情况,分别为不翻转第i个数字就可以和前一个数字保持递增关系,另一种,则是表示从前一个数对的相反的奇偶性和翻转的状态转移过来。所以这两种情况需要分开处理。

最后AC的代码如下所示

#include<bits/stdc++.h>
using namespace std;
#define print pair<int,int>
#define vp vector<print>
#define ALL(x) (x).begin(), (x).end()

void sol(){
    int n;scanf("%d",&n);
    vp pairs(n);
    for(int i=0;i<n;i++){
        scanf("%d",&pairs[i].first);
    }
    for(int i=0;i<n;i++){
        scanf("%d",&pairs[i].second);
    }
    //对于数对(a,b)在这里按照最小的数字来进行排序
    sort(ALL(pairs), [](print a, print b)
         { return min(a.first, a.second) < min(b.first, b.second); });

    vector<vector<int>> dp(4,vector<int>(n,0));

    dp[0][0]=1;
    dp[3][0]=1;
    
    //判断这样子的排序是否可以通过交换来实现
    for(int i=1;i<n;i++){
        //如果这个位置是有序的,那么说明这里没有发生交换
        if(pairs[i-1].first<pairs[i].first and pairs[i-1].second<pairs[i].second){
            dp[0][i] |=dp[0][i-1];
            dp[1][i] |=dp[1][i-1];
            dp[2][i] |=dp[3][i-1];
            dp[3][i] |=dp[2][i-1];
        }
        //如果这个地方是无序的,那么说明这里发生了一次交换
        if(pairs[i-1].first<pairs[i].second and pairs[i-1].second<pairs[i].first){
            dp[0][i] |=dp[2][i-1];
            dp[1][i] |=dp[3][i-1];
            dp[2][i] |=dp[1][i-1];
            dp[3][i] |=dp[0][i-1];
        }
        
    }
    //
    if ((dp[0][n-1] | dp[2][n-1])){
         cout<<"YES"<<endl;
    }
    else{
        cout<<"NO"<<endl;
    }
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        sol();
    }
    return 0;
}

就到这里了。

本文作者:芙芙芙啊

本文链接:https://www.cnblogs.com/fufufuf/p/18697081

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   fufufuf  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起