卡牌游戏 逻辑题

韩梅梅和她的朋友李雷玩卡牌游戏。桌子上有成n堆卡片。每张卡片上都有一个正整数,表示该卡片的价值。

玩家轮流取牌,韩梅梅先手。每个回合,韩梅梅从任意一个非空堆的顶部取牌,李雷从任意一个非空堆的底部取牌。两个人都想最大化他所拿卡片的总价值。当所有堆为空时,游戏结束。

假设李雷和韩梅梅都采取最佳策略,输出最后的赢家。

Input:

第一行包含一个整数N(1N100),表示有n堆卡牌。接下来的n行每行包含第i堆卡牌的信息:

行中的第一个整数是Si(1Si100)表示第i个堆牌的数量;然后是Si个正整数C1,C2,…,Ck,…,CSi(1Ck1000),表示从顶部到底部的卡牌价值序列。

Output:

游戏结束时,如果韩梅梅手中卡牌的价值总和大于李雷手中卡牌的价值总和,输出“win”,如果平局,输出“tie”,否则输出“lose”。

样例输入1

2
1 100
2 1 10

样例输出1

win

样例输入2

3
5 1 2 3 4 5
4 1 2 3 4
8 1 2 3 4 5 6 7 8

样例输出2

lose

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=105;
int n,tot=0;
int c[maxn][maxn],s[maxn];
vector <int> vec;
void solve()
{
    int u=0,v=0;
    int ans=0;
    for(int i=1;i<=n;i++){
        int k=(s[i]+1)/2; //指向最中间的牌
        for(int j=1;j<=k;j++)
            ans+=c[i][j];
        if(s[i]&1){ //第i堆有奇数张牌
            u+=s[i];
            v+=(s[i]+1)/2;
            vec.push_back(c[i][k]);//中间那张牌加入数组
        }
    }
    u=(u+1)/2;
    int tmp=v-u,j=vec.size()-2;    //整个这一块就是要减去奇数堆里中间
    sort(vec.begin(),vec.end());   //那个数里不该小红拿的那几张
    for(int i=0;i<tmp;i++){     //即第二大,第四大…
        if(j>=0){
            ans-=vec[j];
            j-=2;
        }
    }
    if(tot-ans>ans) puts("lose");
    else if(tot-ans==ans) puts("tie");
    else puts("win");
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&s[i]);
        for(int j=1;j<=s[i];j++){
            scanf("%d",&c[i][j]);
            tot+=c[i][j];  //计算总数
        }
    }
    solve();
}

首先我们考虑牌个数为偶数的几堆,那么你会发现,无论小红选哪儿个,小明只要跟着她选对应牌堆底部的,那么他俩总会各选顶部或底部一半牌,而且无论谁先手都是一样,显然这样也是最优的(我不会让你多拿属于我的)。因此现在我们只考虑奇数牌堆:假设一堆牌有5张牌,那么小红可以保证上面两张总是自己的,小明可以保证下面两张总是自己的(小明如果打算选小红的顶部两张,那么只要小红撵着小明的步子选顶部的小明就不会得逞)。但是由于小红先手,因此倘若小明发现小红选了中间数是所有奇数牌堆中最大的那个奇数牌堆,那么小明便可以选择去选中间值第二大的牌堆底部的牌,以此类推,最终小红可以得到 应该属于自己的 + 第一大 + 第三大 + 第五大 + ……, 而小明则可以得到 应该属于自己的 + 第二大 + 第四大 + 第六大 + ……(应该属于自己的指的就是顶部一半和底部一半)。

具体过程可以将中间值抽出来排序然后计算即可。


阅读更多
文章标签: 逻辑题
个人分类: 算法
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭