题目大意:
有2n个人,从0开始编号,按编号奇偶分为两队,循环轮流取一堆有m个石子的石堆,偶数队先手,每个人至少取1个,至多取w[i]个,取走最后一个石子的队伍输。问偶数队是否能赢。
解题思路:
设dp[i][j]表示轮到第i个人,还有j个石子的情况下他所属队伍是否能赢。
那么如果存在一个x,使第i个人取了x个石子后第(i+1)%2n个人无论如何都败,那么他就可以赢;若不存在则输。即是:
dp[i][j]=(dp[(i+1)%2n][j-1]&dp[(i+1)%2n][j-2]&……&dp[(i+1)%2n][j-w[i]])^1;
由于有循环,所以直接dp不好做,改用记忆化搜索。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<vector>
#include<set>
#include<complex>
#define ll long long
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();c!='-'&&(c<'0'||c>'9');c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=20,M=1<<14;
int n,m,w[N],dp[N][M];
int dfs(int i,int j)
{
if(j==0)return dp[i][j]=1;
if(dp[i][j]!=-1)return dp[i][j];
for(int k=1;k<=w[i];k++)
{
if(k>j)break;
if(!dfs((i+1)%n,j-k))return dp[i][j]=1;
}
return dp[i][j]=0;
}
int main()
{
//freopen("lx.in","r",stdin);
while(n=getint())
{
if(!n)break;
n<<=1,m=getint();
for(int i=0;i<n;i++)
w[i]=getint();
memset(dp,-1,sizeof(dp));
cout<<dfs(0,m)<<'\n';
}
return 0;
}