DP+dfs+位运算- -
脑袋都大了- -
因为一个位运算忘记写 ‘=’ 了 ,用了好久才找到错误!!!!!
题目大意:Christine和Matt玩一个游戏.游戏的规则如下:一开始有一列数字(2~20),有的被列出,有的没有列出.从Christine开始,两人轮流划去一个已被列出的数字.每划去一个数字,其他的所有的能被不在这个数列的数字的两两整线性表示的数也自动从数列中退出.到最后,轮到某人而此时数列中没有元素时,这个人就输了.
规定:winning move能将对手置于losing position的决策;而losing position不存在任何winning move的序列.
解题大体思路,定义输入N(N<=20)个数字为一个状态,扫描这些输入的数字,假设选择数字num(扫描到num)后,如果当前状态有数字可选 并且 选择num后搜索剩下的数字组成的状态为没有数字可选,则选择的数字num为winning move.
N(N<=20)个数字组成的状态,想办法寻找能对应这个状态的hash函数,于是可以有一个20位的数(unsigned int 的低20位)表示,这位为1表示这位在输入数字中,为0表示不在输入数字中。则可定义一个数组record表示状态(有数字可选、没有数字可选、不确定),数组大小为(1<<20),数组的每一个下标是一个状态(表示一组输入),下标对应的值为状态。
#include <cstdio>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <fstream>
#include <math.h>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn =21;
int record[1 << maxn];
int n;
int mapstate(bool *s)
{
int index=0;
for(int i=2;i<maxn;i++)
{
if(s[i])
index |= 1;
index <<= 1;
}
return (index >> 1);
}
bool dfs(bool *s,int x) //true可以赢,false不能赢
{
bool cur[maxn];
memcpy(cur,s,maxn);
cur[x]=false;//把目前编号 标记用过
for(int i=2;i+x<21;i++)
{
if(!cur[i])
cur[i+x]=false;//标记 用过编号+目前编号
}
int index =mapstate(cur);
if(record[index]>0)
return true;
else if(record[index]<0)
return false;
for(int i=2;i<maxn;i++)//对方选i
{
if(cur[i]&&!dfs(cur,i)) //对方能选,且对方能赢
{
record[index]=1;//标记这个状态不可行
return true;//返回不可行
}
}
record[index]=-1;//否则该状态可行
return false;
}
int main()
{ifstream cin("input.txt");
bool vis[maxn];
int kase=1;
while(cin>>n,n)
{
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++)
{int x;
cin>>x;
vis[x]=true;
}
int index=mapstate(vis);
//memset(record,0,sizeof(record));
int ans[maxn];
int a=0;
for(int i=2;i<maxn;i++)
{
if(vis[i]&&!dfs(vis,i))//遍历所有值,如果这个数可以选,并且选了能赢
{
ans[a++]=i;//记录下来,一会输出
}
}
printf("Test Case #%d\n",kase++);
if(a==0) {
record[index]=-1;
printf("There's no winning move.\n");
}
else {
printf("The winning moves are:");
for(int i=0;i<a;i++)
printf(" %d",ans[i] );
cout<<endl;
}
cout<<endl;
}
return 0;
}