poj 1143 Number Game
http://poj.org/problem?id=1143
第一道博弈题,纠结了好久好久好久……
题意:给定2到20的数,当选择一个数后,次数的倍数不可再选,已选的数的和不可再选,例如选择了2和3,则2*2, 2*3, 2*4,(2+3) , (2*2+ 3*5)...这些数都不能再选
问最先走那步可必胜
对于一个状态(已走好),若能走到一个必胜的状态,则下一个人一定按必胜策略走,所以此状态必败
若一个状态无论怎样都走不到一个必胜的状态,则次状态必胜
状态0x1fffff,即所有数字全选情况下,为必胜
分享一组数据19 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
答案是 5 7 12 16 18 19
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int vis[2100000], n, value, num[25]; //vis用来记录各个状态,1为必胜,-1必败,0未搜索过
int p2[25];
int add(int v, int nn){ //当加入某个数后,有哪些数相当于同时加了进去
int i, j, aa= nn;
v|= (p2[aa]);
for( i= 2; i<= 20; i++){
if( ((v & (p2[i]))) && ( aa+ i <= 20)){
v|= p2[i+aa];
}
}
return v;
}
bool dfs( int value ){
if( vis[value]){
if( vis[value]== -1) return false;
return true;
}
int i, v;
for( i= 2; i<= 20; i++){
if( (value&p2[i])== 0){
v= add(value, i);
if( dfs(v)){
vis[value]= -1;
return false;
}
}
}
vis[value]= 1;
return true;
}
int main(){
// freopen("1.txt", "r", stdin);
int i, j, k, v, a, b, text= 1, flag;
memset( vis, 0, sizeof( vis));
for( i=0; i<22; i++)
p2[i]= (1<<i);
while( scanf("%d", &n ) && n){
printf("Test Case #%d\n", text++);
value= 0x1fffff; //0~20共21位,1为已选,0为未选, 0x1fffff为全选状态
for( i=0; i<n; i++){
scanf("%d", &a);
value-= p2[a]; // value 为初始状态
num[i]= a;
}
//sort(num, num +n);
//n=unique(num, num+n)-num; //有序数组去重,若从1开始,则n=unique(num+1, num+1+n)-(num+1)括号不可化简掉
flag= 0;
vis[0x1fffff]= 1;
for( i=0; i<n; i++){
v= add(value, num[i]);
if(dfs( v)) {
if( flag== 0){
printf("The winning moves are:");
flag= 1;
}
printf(" %d", num[i]);
}
}
if( flag== 0) printf("There's no winning move.");
printf("\n\n");
}
return 0;
}