题目描述
题意
两个人玩游戏.
有 n n n个整数, 两个人轮流取数, 直到 n n n个数取完为止.
问: 如果规定先手最终取得的数之和为偶数, 则该先手获胜. 问, 先手是否获胜.
思路
dp
- 首先意识到: 数的大小不重要, 重要的是 n n n个数中奇数和偶数的个数, 设他们分别为 c n t 1 cnt1 cnt1和 c n t 0 cnt0 cnt0
- 考虑两种最简单的特殊情况,初始时 c n t 0 = 0 cnt0=0 cnt0=0或 c n t 1 = 0 cnt1=0 cnt1=0, 很容易想到这两种情况下的解
- 除了以上两种情况, 先手第一步有两个选择:
- 选择一个奇数
- 选择一个偶数
以上两种选择情况, 只要有一种最终可以使此人拿到偶数和, 此人就获胜.- 那么就可以将此人先手是否获得偶数和 e v e n [ c n t 1 ] [ c n t 0 ] even[cnt1][cnt0] even[cnt1][cnt0]根据他选择的是奇数还是偶数进行转移, 即他的对手想要获胜的情况.
例如: 先手A首轮选择了奇数, 那么他的对手B想要获胜, 就要阻止A在后面 [ c n t 1 − 1 ] [ c n t 0 ] [cnt_1-1][cnt_0] [cnt1−1][cnt0]的数中再累计拿到奇数和构成偶数, 那么B就要根据 c n t 1 − 1 cnt_1-1 cnt1−1的奇偶进行选择
code
#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
typedef pair<int, int > PII;
typedef long long ll;
const int N = 110;
bool odd[N][N];
bool even[N][N];
void solv()
{
int n;
cin >> n;
int x;
int cnt1 = 0;
for(int i = 0; i < n; i ++)
{
cin >> x;
if(x & 1) cnt1 ++;
}
int cnt0 = n - cnt1;
if(even[cnt1][cnt0]) cout << "Alice\n";
else cout << "Bob\n";
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T;
cin >> T;
for(int i = 1; i <= 100; i ++)
{
even[i][0] = (i + 1) / 2 & 1 ^ 1;
even[0][i] = 1;
odd[i][0] = (i + 1) / 2 & 1;
odd[0][i] = 0;
}
for(int i = 0; i <= 100; i ++)
for(int j = 0; j <= 100; j ++)
if(i && j)
{
if(i&1)
{
even[i][j] |= !even[i - 1][j];
even[i][j] |= !even[i][j - 1];
odd[i][j] |= !odd[i - 1][j];
odd[i][j] |= !odd[i][j - 1];
}
else
{
even[i][j] |= !odd[i - 1][j];
even[i][j] |= !odd[i][j - 1];
odd[i][j] |= !even[i - 1][j];
odd[i][j] |= !even[i][j - 1];
}
}
while(T --)
{
solv();
}
return 0;
}
注意与总结
看数据范围往往可以猜测用什么算法解决问题.