題目描述
給你n個節點的凸包(未連線),每次選擇兩個點連一條線,不能與之前出現的線有相交。當出現一個凸包的時候遊戲結束
誰最後無法移動了就輸了,現在問 是先手必勝還是後手必勝。
HDU描述的是當出現一個三角形時 遊戲結束。其實是一個意思,HDU的範圍大一點 涉及循環節。
做法:SG函數。如果你是第一次聽說SG函數。看鏈接:知乎
而對於這題的分析呢。
每一個點集都可以被一條直線分割成一個包含兩部分的子局面,根據SG函數從前往後推
那麼對於當前局面SG(x),它的後繼局面爲 任選兩個點連接後,得到的局面是被該線分割成兩部分,如果下一個人在這條線的兩個端點任意一個出發再連一條線,則下下個人就可以連成三角形使得遊戲結束,因此下一個人必不會再從這兩個端點連線。因此後繼局面爲sg(i)與sg(x-i-2) [0<=i<=x-2]
即得到sg函數爲: SG(X)=mex{sg(i)^sg(x-i-2)} [0<=i<=x-2]
對於GYM的這道題 直接推SG數組即可。
對於HDU 的打表找循環節
由於數據的n很大,打表出前1000項觀察可以得到循環節。。。打表
代碼參考來自:博客
/*
因爲一條直線把當前局面分割成兩個子局面,
i-2是連接完直線後剩下的點,所以兩個子局面的異或就是sg[j]^sg[i-2-j]
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn=5010;
int sg[maxn],s[maxn];
void SG()
{
sg[1]=0,sg[2]=1;
for(int i=3;i<=5000;i++){
memset(s,0,sizeof(s));
for(int j=0;j<=i-2;j++) //所有子局面
s[(sg[j]^sg[i-2-j])]=1;
for(int j=0;;j++){
if(!s[j]){
sg[i]=j; break;
}
}
}
}
int main()
{
SG();
int t; scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
if(sg[n]) puts("First");
else puts("Second");
}
return 0;
}
HDU 題 代碼參考:博客
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
using namespace std;
const int MAXN = 1000 + 10;
int vis[MAXN];
int SG[MAXN];
int mex(int x)
{
if(SG[x] != -1)
return SG[x];
memset(vis, 0, sizeof(vis));
for(int i=0;i<=x-2;i++)
vis[mex(i) ^ mex(x-2-i)] = 1;
for(int i=0;;i++) if(!vis[i])
{
SG[x] = i;
break;
}
return SG[x];
}
int main()
{
memset(SG, -1, sizeof(SG));
for(int i=0;i<200;i++)
SG[i] = mex(i);
int T;
scanf("%d", &T);
while(T--)
{
int n, x;
scanf("%d", &n);
int ans = 0;
for(int i=0;i<n;i++)
{
int x;
scanf("%d", &x);
if(x < 100) ans ^= (SG[x]);
else
{
x -= 60;
x %= 34;
ans ^= SG[x + 60];
}
}
if(ans) printf("Carol\n");
else printf("Dave\n");
}
return 0;
}