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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章