计蒜客 16877 卡牌游戏
题目链接:https://nanti.jisuanke.com/t/16877
题目大意:桌子上有N堆牌,每堆牌有Si张,每张牌上有个数。小明和小红玩游戏,小红女士优先,每次从任意一个牌堆顶部取出一张,小明长得丑,每次从任意一个牌堆底部取一张。假设他俩都按照最优的方式取牌,那么谁最后得到的所有牌的数字的和大。
解题思路:首先我们考虑牌个数为偶数的几堆,那么你会发现,无论小红选哪儿个,小明只要跟着她选对应牌堆底部的,那么他俩总会各选顶部或底部一半牌,而且无论谁先手都是一样,显然这样也是最优的(我不会让你多拿属于我的)。因此现在我们只考虑奇数牌堆:假设一堆牌有5张牌,那么小红可以保证上面两张总是自己的,小明可以保证下面两张总是自己的(小明如果打算选小红的顶部两张,那么只要小红撵着小明的步子选顶部的小明就不会得逞)。但是由于小红先手,因此倘若小明发现小红选了中间数是所有奇数牌堆中最大的那个奇数牌堆,那么小明便可以选择去选中间值第二大的牌堆底部的牌,以此类推,最终小红可以得到 应该属于自己的 + 第一大 + 第三大 + 第五大 + ……, 而小明则可以得到 应该属于自己的 + 第二大 + 第四大 + 第六大 + ……(应该属于自己的指的就是顶部一半和底部一半)。
具体过程可以将中间值抽出来排序然后计算即可。
代码:
1 const int maxn = 1e2 + 5; 2 int n, tot = 0; 3 int c[maxn][maxn], s[maxn]; 4 vector<int> vec; 5 6 void solve(){ 7 int u = 0, v = 0; 8 int ans = 0; 9 for(int i = 1; i <= n; i++){ 10 int k = (s[i] + 1) / 2; 11 for(int j = 1; j <= k; j++) 12 ans += c[i][j]; 13 if(s[i] & 1){ 14 u += s[i]; 15 v += (s[i] + 1) / 2; 16 vec.push_back(c[i][k]); 17 } 18 } 19 u = (u + 1) / 2; 20 int tmp = v - u, j = vec.size() - 2; 21 sort(vec.begin(), vec.end()); 22 for(int i = 0; i < tmp; i++){ 23 if(j >= 0) { 24 ans -= vec[j]; 25 j -= 2; 26 } 27 } 28 if(tot - ans > ans) puts("lose"); 29 else if(tot - ans == ans) puts("tie"); 30 else puts("win"); 31 } 32 int main(){ 33 scanf("%d", &n); 34 for(int i = 1; i <= n; i++){ 35 scanf("%d", &s[i]); 36 for(int j = 1; j <= s[i]; j++){ 37 scanf("%d", &c[i][j]); 38 tot += c[i][j]; 39 } 40 } 41 solve(); 42 }
题目:
韩梅梅和她的朋友李雷玩卡牌游戏。桌子上有成n堆卡片。每张卡片上都有一个正整数,表示该卡片的价值。
玩家轮流取牌,韩梅梅先手。每个回合,韩梅梅从任意一个非空堆的顶部取牌,李雷从任意一个非空堆的底部取牌。两个人都想最大化他所拿卡片的总价值。当所有堆为空时,游戏结束。
假设李雷和韩梅梅都采取最佳策略,输出最后的赢家。
Input:
第一行包含一个整数N(1 ≤ N ≤ 100),表示有n堆卡牌。接下来的n行每行包含第i堆卡牌的信息:
行中的第一个整数是Si(1 ≤ Si ≤ 100)表示第i个堆牌的数量;然后是Si个正整数C1,C2,…,Ck,…,CSi(1 ≤ Ck ≤ 1000),表示从顶部到底部的卡牌价值序列。
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