题目链接:取(2堆)石子游戏 - HDU 2177 - Virtual Judge (vjudge.net)
这显然是一道威佐夫博弈题目,如果先手必胜,他让我们输出所有的先手操作方式,我把所有情况的讨论写在了一张纸上,大家可以结合代码,参考一下:
下面是代码:
#include#include #include #include #include using namespace std; const double e=(sqrt(5.0)+1)/2; bool check(long long a,long long b) { if((long long)(e*(b-a))==a) return true; return false; } int main() { long long n,m; while(1) { scanf("%lld%lld",&n,&m); if(n==0&&m==0) break; long long minn=min(n,m),maxx=max(n,m); if((long long)((maxx-minn)*e)==minn) puts("0");//必输情况 else { puts("1"); if(minn>(long long)((maxx-minn)*e)) { //首先输出从两堆中同时取的情况 printf("%lld %lldn",(long long)((maxx-minn)*e),(long long)((maxx-minn)*e)+maxx-minn); for(int i=maxx/2;i<=(int)(maxx*0.7);i++) if(check(i,maxx)) { printf("%lld %lldn",i,maxx); break; } if(minn==maxx) continue;//防止被重复计算 for(int i=minn/2;i<=(int)(minn*0.7);i++) if(check(i,minn)) { printf("%lld %lldn",i,minn); break; } } else { if(minn==((long long)((long long)(minn/e)*e)+1)) printf("%lld %lldn",minn,minn+(long long)(minn/e)+1); for(int i=minn/2;i<=i<=(int)(minn*0.7);i++) if(check(i,minn)) { printf("%lld %lldn",i,minn); break; } } } } return 0; }
除了上面的做法外,我们还可以通过打表来做,不过依据的原理还是我在所讨论的几种情况,下面是打表过的代码:
#include#include #include #include #include using namespace std; const int N=2e6+500; int l[N],r[N],cnt; bool vis[N]; void init() { memset(vis,false,sizeof vis); l[cnt]=0;r[cnt]=0; for(int i=1;i<=N/2;i++) { if(!vis[i]) { l[++cnt]=i; r[cnt]=i+cnt; vis[i+cnt]=true; } } } int findl(int x) { int ans=lower_bound(l,l+cnt+1,x)-l; if(l[ans]==x) return ans; return -1; } int findr(int x) { int ans=lower_bound(r+1,r+cnt+1,x)-r; if(r[ans]==x) return ans; return -1; } int main() { init(); int n,m; while(1) { scanf("%d%d",&n,&m); if(n==0&&m==0) break; int minn=min(n,m),maxx=max(n,m); int f1l=findl(minn),f1r=findr(minn),f2l=findl(maxx),f2r=findr(maxx); if(f1l!=-1&&f1l==f2r) puts("0"); else { puts("1"); if(minn>l[maxx-minn])//输出从两堆石子中取同样数量的石子的情况 printf("%d %dn",l[maxx-minn],r[maxx-minn]); if(f1l!=-1&&r[f1l]



