题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=39
记忆化搜索。这个题也相当郁闷,开始用f[i][j]表示第 i 次选择时 j 是不是必胜数,如果选择 j 后没有必胜数,那么 j 就是必胜数,反之不是必胜数,用DFS(i, j)来判断,总是不对,郁闷死了。后来看题解,用二进制状态压缩,于是改成二进制状态压缩,搜索的方式也改成和题解一样的,只是对于除去非法数字的方法不同,但仍然是错,恶心。
#include<iostream>
#include<cstring>
using namespace std;
int f[1<<21];
bool g[1<<21];
bool used[25];
void Chenge(int i)
{
int j = i;
while (j <= 20)
{
used[j] = true;
//for (int k=2; k<=20; k++)
// if (used[k] && k+j <=20)
// used[k+j] = true;
j += i;
}
}
void Back(bool tmp[])
{
for (int i=0; i<25; i++)
used[i] = tmp[i];
}
int work()
{
int now = 0;
for (int i=1; i<=20; i++)
if (!used[i])
now += 1<<i;
return now;
}
int DFS()
{
int now = work();
if (g[now])
return f[now];
g[now] = true;
f[now] = 0;
bool tmp[25];
for (int i=0; i<25; i++) tmp[i] = used[i];
for (int i=2; i<=20; i++)
if (!used[i])
{
Chenge(i);
int New = work();
int x = DFS();
if (x == 0)
f[now] += (1<<(i-1)) ;
Back(tmp);
}
return f[now];
}
int main()
{
int t;
int k = 0;
cin>>t;
while (t--)
{
k++;
memset(used,true,sizeof(used));
memset(f,false,sizeof(f));
memset(g,false,sizeof(g));
int a;
cin>>a;
for (int i=0; i<a; i++)
{
int x;
cin>>x;
used[x] = false;
}
int ans = 0;
cout<<"Scenario #"<<k<<":"<<endl;
if (!(ans = DFS()))
{
cout<<"There is no winning move."<<endl;
continue;
}
cout<<"The winning moves are:";
for (int i=1; i<=20; i++)
{
if (ans % 2 == 1)
cout<<' '<<i;
ans >>= 1;
if (ans == 0)
break;
}
cout<<"."<<endl;
cout<<endl;
}
}
后来还是看题解。用二进制表示,从低位到高位,分别表示1到20是否在集合中。要判断一个数 j 是不是在集合中,只要&一下2^j就就可以了,结果位1,则在集合中,否则不在集合中。只是对于除去非法数字的方法没有看懂,郁闷。
#include<iostream>
#include<cstring>
using namespace std;
int f[1<<21];
int mask[1<<21];
int DFS(int num)
{
if (f[num] == -1)
{
f[num] = 0;
for (int i=2; i<=20; i++)
if ((num & mask[i]) != 0)
{
int tmp = num;
int j = i;
while (j<=20)
{
int buf = (((num | 1) << j) | (mask[j]-1));
tmp &= buf;
j += i;
}
int x = DFS(tmp);
if (x == 0)
f[num] += mask[i];
}
}
return f[num];
}
int main()
{
int t;
int k = 0;
cin>>t;
while (t--)
{
k++;
memset(f,255,sizeof(f));
for (int i=1; i<=20; i++)
mask[i+1] = 1<<i;
int a = 0,n;
cin>>n;
for (int i=0; i<n; i++)
{
int x;
cin>>x;
a |= mask[x];
}
int ans = DFS(a);
cout<<"Scenario #"<<k<<":"<<endl;
if (ans == 0)
{
cout<<"There is no winning move."<<endl;
cout<<endl;
continue;
}
cout<<"The winning moves are:";
for (int i=1; i<=20; i++)
{
if (ans % 2 == 1)
cout<<' '<<i;
ans >>= 1;
if (ans == 0)
break;
}
cout<<"."<<endl;
cout<<endl;
}
}