HDU 6655 Just Repeat【博弈】【排序】

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6655
题目大意:两个人打牌,每人手里各种颜色牌若干张,每次出一张,打牌规则是:若某种颜色的牌出手了,则对方不能出这种颜色的牌,最后不能出牌者输。
题解思路:
每个人优先出的牌的颜色肯定是场上没出过的, 对方也持有的, 并且两个人手中持有数量最多的牌,这是因为对方持有的越多意味着可以封掉更多的牌, 而自己手里的越多意味着可以防止自己更多的牌被封掉.
因此, 对所有两个人手里都持有的颜色的牌数进行统计, 从大到小依次分配给第一, 第二个玩家. 如果此时第一个玩家手里的牌数 >> 第二个玩家, 则第一个玩家胜利, 否则第二个玩家胜利. 到此为止, 问题转换成另一个问题, 就是有一堆东西, 每个东西有两个值, 玩家AA拿到这个东西的收 益是 aia_i, 玩家BB 拿到的收益是 bib_i.两人依次拿.求最优策略下两人的各自收益.这是一个经典问题, 答案就是按照 ai+bia_i + b_i 排序模拟一下就好了.
AC代码:

#include<bits/stdc++.h>
using namespace std;
unsigned long long n,m,p,a[100010],b[100010];
unsigned long long k1, k2,mod;
unsigned long long rng() {
    unsigned long long k3 = k1, k4 = k2;
    k1 = k4;
    k3 ^= k3 << 23;
    k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
    return k2 + k4;
}
void ger1(){
    for (int i = 0; i < n; ++i){
        a[i] = rng() % mod;
    }
}
void ger2(){
    for (int i = 0; i < m; ++i){
        b[i] = rng() % mod;
    }
}
int ca,cb;
using pp = pair<unsigned long long, int>;
bool cmp(pp x,pp y){//sum重载的是按照这种牌的总数从大到小排序
    return x.first+x.second>y.first+y.second;
}
pp cnta[100010],cntb[100010];//cnta表示QQ手中每种牌对应的颜色和数量,cntb表示CC手中每种牌对应的颜色和数量
pp sum[100010];//sum表示每种牌的总数从大到小排序,first对应的是QQ手中这种牌的数量,second对应的是CC手中这种牌的数量
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%llu%llu%llu",&n,&m,&p);
        if(p==1){
            for(int i=0;i<n;i++){
                scanf("%llu",&a[i]);
            }
            for(int i=0;i<m;i++){
                scanf("%llu",&b[i]);
            }
        }
        else{
            
            scanf("%llu%llu%llu",&k1,&k2,&mod);
            ger1();
            scanf("%llu%llu%llu",&k1,&k2,&mod);
            ger2();
        }
        sort(a,a+n);//先对a,b进行排序,使得相同颜色的牌相邻,方便后续对每种牌计数操作
        sort(b,b+m);
        ca=0,cb=0;//ca表示QQ手中实际牌的种类,cb表示CC手中实际牌的种类
        int i=1,la=0;
        while(i<n){
            while(i<n&&a[i]==a[i-1]){
                i++;
            }
            cnta[ca++]=pp(a[la],i-la);
            la=i;
            i++;
        }
        if(a[n-1]!=a[n-2]){
            cnta[ca++]=pp(a[n-1],1);
        }
        int j=1,lb=0;
        while(j<m){
            while(j<m&&b[j]==b[j-1]){
                j++;
            }
            cntb[cb++]=pp(b[lb],j-lb);
            lb=j;
            j++;
        }
        if(b[m-1]!=b[m-2]){
            cntb[cb++]=pp(b[m-1],1);
        }
        int qs=0,cs=0;//打牌时qs表示QQ手中可出的牌的数量,cs表示CC手中可出的牌的数量
        int csum=0;
        for(int ii=0,jj=0;ii<ca||jj<cb;){
            if(ii<ca&&jj<cb&&cnta[ii].first==cntb[jj].first){
                sum[csum++]=pp(cnta[ii++].second,cntb[jj++].second);//将相同颜色牌计入到sum中
            }
            else if(ii==ca||(jj<cb&&cntb[jj].first<cnta[ii].first)){
                cs+=cntb[jj++].second;//QQ手里没牌了,那么此时CC手里的牌都是自己的
            }
            else{
                qs+=cnta[ii++].second;//CC手里没牌了,那么此时QQ手里的牌都是自己的
            }
        }
        sort(sum,sum+csum,cmp);//对sum进行总数排序
        for(int k=0;k<csum;k++){
            if(k%2==0){
                qs+=sum[k].first;//QQ、CC每次都先拿剩下牌中总数最多的牌,这样保证最优,但是QQ先拿
            }
            else{
                cs+=sum[k].second;
            }
        }
        if(qs>cs){
            printf("Cuber QQ\n");
        }
        else{
            printf("Quber CC\n");
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章