codeforces Round #611 (Div3) 部分题解

传送门

C 题:

题意:共有n个朋友每个人必须送出一个礼物和接受到一个礼物(不能送给本人)。当fi=0表示不知道送礼物给谁,因此要你求出fi=0时具体送出礼物给谁,任一方案

思路::
(1):先筛选出哪些人没有收到礼物,并将其压入set中。第一遍可以先扫描出那些既没有收到礼物又没有送出礼物的人,那我们就优先对他们进行礼物分配,从set中找出第一个大于它的数,如果没有则用begin(),同时将其从set中删除,(为啥要优先考虑呢?这样优先操作后必然使可以每个人送出的礼物不同,可以避免当仅剩某一个礼物时只分配给其本人的情况
举个例子 :
6
2 0 1 5 3 0)
然后在对剩下未处理的进行分配礼物
详情见代码

#include<bits/stdc++.h>
#define ll long long
#define pii pair<double,double>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn=2e5+5;
const int inf=1e9+7;
 
set<int>s;
int vis[maxn];
bool vi[maxn];
 
int main()
{
    int n,x;
    scanf("%d",&n);
    mem(vi,0);
    for(int i=1;i<=n;i++){
        scanf("%d",&vis[i]);
        vi[vis[i]]=1;
    }
    for(int i=1;i<=n;i++){
        if(vi[i]==0){s.insert(i);}
    }
    for(int i=1;i<=n;i++){
        if(vis[i]==0&&vi[i]==0){
            auto it=s.upper_bound(i);
            if(it==s.end()){
                vis[i]=*s.begin();s.erase(s.begin());
            }
            else{
                vis[i]=*it;s.erase(*it);
            }
        }
    }
    for(int i=1;i<=n;i++){
        if(vis[i]==0){
            auto it=s.upper_bound(i);
            if(it==s.end()){vis[i]=*s.begin();s.erase(s.begin());}
            else{vis[i]=*it;s.erase(*it);}
            printf("%d ",vis[i]);
        }
        else{
            printf("%d ",vis[i]);
        }
    }
    return 0;
}

(2):首先将分配了礼物的人标记,依次遍历时 fi 时,如果等于0我们就将i压入vector(pos)中,同时寻找未标记的分配给他。第二遍循环检查是否有送出礼物给本人的,若有判断当前 i 是否等于pos[0],是则交换(f[i],f[pos[1]]),反之与f[pos[1]]交换。

#include<bits/stdc++.h>
#define ll long long
#define pii pair<double,double>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn=2e5+5;
const int inf=1e9+7;
 
vector<int>pos;
int f[maxn];
bool vis[maxn];
 
int main()
{
    int n;
    scanf("%d",&n);
    mem(vi,0);
    for(int i=1;i<=n;i++){
        scanf("%d",&f[i]);
        vis[f[i]]=1;
    }
    for(int i=1,j=1;i<=n;i++){
        if(f[i]==0){
            pos.push_back(i);
            while(vis[j])j++;
            f[i]=j;vis[j]=1;
        }
    }
    for(int i=1;i<=n;i++){
        if(f[i]==i){
            swap(f[i],i==pos[0]?f[pos[1]]:f[pos[0]]);
        }
    }
    for(int i=1;i<=n;i++){
        printf("%d%c",f[i],"\n"[i==n]);
    }
    return 0;
}

D题:

题意:有n个已知圣诞树位置,要求你再分配m个人所在位置,使得所有新分配的位置与其距离最近的圣诞树距离差值之和最小。

思路:直接上bfs搜索啊,将n个圣诞树的位置作为起点,进行距离扩张(x+1,x+2,x+3…)

#include<bits/stdc++.h>
#define ll long long
#define pii pair<double,double>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn=2e5+5;
const int inf=1e9+7;
 
int a[maxn];
map<int,bool>mp;//标记已访问过的点
queue<int>q;
int b[maxn];
 
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        mp[a[i]]=1;
    }
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++){
        q.push(a[i]);
    }
    ll ans=0;
    int ant=0,tt,kk=1,pp=a[n];
    while(!q.empty()){
        tt=q.front();q.pop();
        if(mp[tt-1]!=1){m--;b[++ant]=tt-1;q.push(tt-1);ans+=kk;mp[tt-1]=1;}
        if(m<1)break;
        if(mp[tt+1]!=1){m--;b[++ant]=tt+1;q.push(tt+1);ans+=kk;mp[tt+1]=1;}
        if(m<1)break;
        if(tt==pp){pp++;kk++;}
    }
    printf("%lld\n",ans);
    for(int i=1;i<=ant;i++){
        printf("%d ",b[i]);
    }
    return 0;
}

E题:

题意:给你n个位置,每个位置最多执行一次移动 ( xi+1 或 xi-1 或 xi+0 );当所有位置进行移动后,问你此时的位置出现最少和最多的种类分别是多少

思路:贪心
最少的情况:很明显我们尽量使两边的向中间靠拢,中间的向两边靠拢。对当前考虑,如果 -1 或者 +1 或者 +0 被占有的话跳过,反之出于对后面的数考虑,我们肯定向右移动;
最多的情况:我们尽量使他们向两边扩张。对当前依次考虑,如果 -1 未被占有,左移动占有;如果 +0 未被占有 不动占有;如果 +1 未被占有右移动占有;

#include<bits/stdc++.h>
#define ll long long
#define pii pair<double,double>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn=2e5+5;
const int inf=1e9+7;

int a[maxn];
bool vis[maxn];

int main()
{
    mem(vis,0);
    int n,ans=0,ant=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++){
        if(vis[a[i]-1]||vis[a[i]]||vis[a[i]+1]){continue;}
        ans++;vis[a[i]+1]=1;
    }
    mem(vis,0);
    for(int i=1;i<=n;i++){
        if(!vis[a[i]-1]){vis[a[i]-1]=1;ant++;}
        else if(!vis[a[i]]){vis[a[i]]=1;ant++;}
        else if(!vis[a[i]+1]){vis[a[i]+1]=1;ant++;}
    }
    printf("%d %d\n",ans,ant);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值