2019杭电暑期多校第七场 J:Just Repeat(贪心+思维)

本文探讨了一种算法竞赛题目中出现的博弈问题,通过分析两人轮流出牌的策略,将问题转化为收益分配的经典问题。文章详细介绍了如何通过统计双方共有的牌的数量,按照贡献总和排序来确定最优策略,最终决定胜者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题解】

题意:两个人轮流出牌,每次出牌的颜色不能跟对方出过的牌的颜色一致,如果谁出不了牌就输了。

思路:每个人优先出的牌的颜色肯定是:场上没出过的, 对方也持有的,并且两个人手中持有数量最多的牌。对方持有的越多意味着可以封掉更多的牌,而自己手里的越多意味着可以防止自己更多的牌被封掉。因此,对所有的两个人手里都持有的颜色的牌数进行统计, 从大到小依次分配给第一、二个玩家。如果此时第一个玩家手里的牌数 > 第二个玩家,则第一个玩家胜利, 否则第二个玩家胜利。 到此为止,问题转换成另一个问题:有一堆东西,每个东西有两个值,A 拿到这个东西的收益是 ai,B 拿到的收益是 bi,两人依次拿,求最优策略下两人的各自收益。这是一个经典问题,答案就是按照 ai + bi 排序模拟一下就好了。

【代码】

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
typedef pair<int, int> P;
const int N=2e5+10;
P A[N],B[N],C[N];
ULL a[N];
ULL k1,k2;
ULL rng(){
    ULL k3=k1,k4=k2;k1=k4;k3^=k3<<23;k2=k3^k4^(k3>>17)^(k4>>26);
    return k2+k4;
}
void init(P T[],int& n,int p)
{
    if(p==1){
        for(int i=0;i<n;i++) scanf("%llu",&a[i]);
    }
    else{
        int mod; scanf("%llu%llu%d",&k1,&k2,&mod);
        for(int i=0;i<n;i++) a[i]=rng()%mod;
    }
    sort(a,a+n);
    int l=0;
    for(int i=0,j=0;i<n;i=j){
        while(j<n&&a[j]==a[i]) j++;
        T[l++]=P(a[i],j-i);
    }
    n=l;
}
bool cmp(const P& a, const P& b)
{
    return a.first+a.second>b.first+b.second;
}
int main()
{
    int T; scanf("%d",&T);
    while(T--){
        int n,m,p; scanf("%d%d%d",&n,&m,&p);
        init(A,n,p); init(B,m,p);
        int SA=0,SB=0,k=0;
        int p1=0,p2=0;
        while(p1<n||p2<m){
            if(p1<n&&p2<m&&A[p1].first==B[p2].first) C[k++]=P(A[p1++].second,B[p2++].second); //双方手里的相同颜色的牌的数目
            else if(p1==n||(p2<m&&A[p1].first>B[p2].first)) SB+=B[p2++].second; //如果B[p2].first比较小,那么说明不可能有相同颜色了
            else if(p2==m||(p1<n&&A[p1].first<B[p2].first)) SA+=A[p1++].second; //同上,所以直接加上贡献,这是互不影响的
        }
        sort(C,C+k,cmp); //按照贡献和排序
        for(int i=0;i<k;i++){
            if(i%2==0) SA+=C[i].first;
            else SB+=C[i].second;
        }
        if(SA>SB) puts("Cuber QQ"); //Cuber QQ 先手
        else puts("Quber CC");
    }
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值