POJ1143 Number Game(DP)
题目大意:
就是说两个人交换选一些数,如果a,b被选过了,那么k*a+m*b(k,m >=0 )这样的数就不能再被选择,现在给你一些还没有选的数,问选哪个数可以使你必胜
如样例:
2 5
如果你选2,由于3已经选过了,而2+3=5,所以5也不能备选择。所以选2就为必胜的选择
我的思路:
,这道题的最初要想到的就是,由于题目的给的数的范围很小,<=20。所以表示这些数的集合就可以用二进制模拟。
用一个DP数组就可以存下,他有两个值就是0和1,表示必胜和必输两个状态。详见代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #define MAX(a,b) (a) > (b)? (a):(b) 6 #define MIN(a,b) (a) < (b)? (a):(b) 7 #define mem(a) memset(a,0,sizeof(a)) 8 #define INF 1000000007 9 #define MAXN 1<<20 10 #define MACN 20 11 12 #define JUDGE (((1<<(ma[j]-ma[i]-1)) & ~st) &&(ma[j]-ma[i] != 1)) || (!((ma[j]+1)%(ma[i]+1))) 13 14 using namespace std; 15 16 int DP[MAXN],ma[MACN],vis[MAXN]; 17 int N; 18 19 int search(int k,int state) 20 { 21 if(vis[state])return DP[state]; 22 vis[state]=1; 23 if(state == 0)return 0; 24 if(k == 1)return DP[state] = 1;//集合里面只有一个数了,一定是必胜状态 25 int i, j, key=0; 26 for(i = 0;i < N;i ++ ) 27 { 28 int st = state; 29 if((1<<ma[i]) & state) 30 { 31 for(j = i+1; j < N; j ++ )//首先去掉ma[i]的倍数和(ma[j]-ma[i])已经使用了的ma[j] 32 { 33 if( ((1<<ma[j]) & st) && (JUDGE) ) 34 { 35 st ^= (1<<ma[j]); 36 } 37 } 38 key = search(k-1, st^(1<<ma[i])); 39 if( !key )return DP[state]=1;//一旦下一个状态有必输的状态,那这个状态就是必胜的 40 } 41 } 42 return DP[state]=0; 43 } 44 45 int main() 46 { 47 int cas=1; 48 while(~scanf("%d",&N) && N) 49 { 50 mem(vis); 51 int i,j,state=0; 52 int ans[MACN]={0},count=0; 53 54 for(i=0;i<N;i++) 55 { 56 scanf("%d",&ma[i]); 57 ma[i]--; 58 state ^= (1<<ma[i]);//初始化状态集合state 59 } 60 61 sort(ma,ma+N); 62 for(i = 0; i < N; i ++ ) 63 { 64 int st = state; 65 for(j = i+1; j < N; j ++ ) 66 { 67 if(JUDGE) st ^= (1<<ma[j]); 68 } 69 if(search(N-1, (1<<ma[i])^st) == 0) 70 { 71 ans[count++] = ma[i]+1; 72 } 73 } 74 75 printf("Test Case #%d\n", cas++); 76 if(!count)printf("There's no winning move.\n\n"); 77 else { 78 printf("The winning moves are:"); 79 for(i=0;i<count;i++) 80 printf(" %d", ans[i]); 81 printf("\n\n"); 82 } 83 } 84 return 0; 85 }