http://acm.hdu.edu.cn/showproblem.php?pid=4672 ---> 题目地址
http://www.cnblogs.com/xin-hua/p/3259388.html----> 我参考的代码
Present Day, Present Time
题意:有 N 堆石子和 M 个石子回收站,每回合操作的人可以选择一堆石子,从中拿出一些放到石子回收站里(可以放进多个回收站,每个回收站可以使用无数次)
,但每个石子回收站每次
只能接收特定数量的石子。不能操作的输。如果有人操作完之后,有任意一堆石子无法完全回收,
那么他直接输。
一个显然的结论是,每个游戏的 SG 值就是用 M 个回收站,完全回收这堆石子可行的最大
操作次数。由于最大的 Bi 比较小,立方暴力背包即可(比较显然的是,maxBi 2 以上的周期是
minBi )
。
刚看题解。你可能不太明白。。 如果你理解题目意思了的话, 只用判断两种情况
1、开始便存在一堆石子无法回收,直接输出play2赢
2、已经知道这些所有石子都能回收了,就算出每一堆的石子异或后看答案是否为0,为0则输出play2赢,否则play1赢。
因为题目数据量太大,然后得发现这样的一个情况,最大的bi<=100,而且当sg算到 max(bi) ^ 2 之后时, 它的呈周期增加的(一个周期加1),因而当数据大于bi的平方时,可以直
接用bi的平方以内的数据计算出来。
算1到bi的平方的sg时,直接暴力算就行。
一堆能被回收的石子的SG值 就是用 M 个回收站,完全回收这堆石子可行的最大操作次数
如果你不懂SG的含义。。可以看下 取石子游戏http://acm.hdu.edu.cn/showproblem.php?pid=1527 ,取石子游戏,每堆的SG值就是其一堆石子的数目
HDU 1846 http://acm.hdu.edu.cn/showproblem.php?pid=1846
HDU 4111 http://acm.hdu.edu.cn/showproblem.php?pid=4111
然后此题就可以算出来了。
标准程序:
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
char player1[111];
char player2[111];
int sg[23333];
int stones[111111];
int b[111];
int main(void)
{
int T = 0, n, m;
while(scanf("%d%d%s%s",&n,&m,player1,player2) != EOF)
{
for(int i = 0;i < n;i++) scanf("%d",&stones[i]);
for(int i = 0;i < m;i++) scanf("%d",&b[i]);
int mx = *max_element(b,b+m);
int mn = *min_element(b,b+m);
int base = mx*mx;
int limit = base+mn+5;
memset(sg,-1,sizeof(int)*(limit+10));
sg[0] = 0;
for(int i = 0;i < m;i++)
{
int x = b[i];
for(int j = 0;j <= limit;j++)
{
if(sg[j] < 0) continue;
if(x+j <= limit && sg[x+j] < sg[j]+1) sg[x+j] = sg[j]+1;
}
}
//for(int i = 0;i <= limit;i++) printf("sg[%d] = %d\n",i,sg[i]);
bool fail = false;
int result = 0;
for(int i = 0;i < n;i++)
{
if(stones[i] <= limit)
{
if(sg[stones[i]] < 0) fail = true;
else result ^= sg[stones[i]];
}
else
{
int down = (stones[i] - base)/mn*mn;
if(sg[stones[i]-down] < 0) fail = true;
else result ^= down/mn + sg[stones[i]-down];
}
}
if(fail) puts(player2);
else puts(result ? player1 : player2);
//else printf("%d\n",result);
}
while(getchar() != EOF);
return 0;
}
我的代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
int a[111111],b[111];
int sg[31111];
int main(){
// freopen("4672.in","r",stdin);
int n,m;
char s1[20],s2[20];
while(scanf("%d %d %s %s",&n,&m,s1,s2)!=EOF){
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
int mm=1e9;
memset(sg,-1,sizeof(sg));
sg[0]=0;
for(int i=0;i<m;i++){
scanf("%d",b+i);
mm=min(mm,b[i]);
for(int j=0;j+b[i]<=10000;j++)
if(-1!=sg[j])
sg[j+b[i]]=max(sg[j+b[i]],sg[j]+1);
}
bool fail=false;
int ret=0;
for(int i=0;i<n;i++){
int t=a[i];
if(t<=10000){
if(sg[t]==-1){
fail=true;
break;
}else{
ret^=sg[t];
}
}else{
t=(t-10000)%mm+10000-mm;
if(sg[t]==-1){
fail=true;
break;
}else{
ret^=(a[i]-t)/mm + sg[t];
}
}
}
if(fail || !ret) puts(s2);
else puts(s1);
}
return 0;
}