HDU 4664 Triangulation【博弈論】

一個平面上有n個點(一個凸多邊形的頂點),每次可以連接一個平面上的兩個點(不能和已經連接的邊相交),如果平面上已經出現了一個三角形,則不能在這個平面上繼續連接邊了。

現在總共有N個平面,每個平面上都有若干點。(就是有N個相同的遊戲同時進行了)。


想法很單純,就是計算出每一個平面上游戲的sg函數值,然後求Nim和就哦了。

sg函數暴力求法:

一個平面上連接點時,不能連接已經有邊的頂點,因爲對方只需要再連接一次就可以組成一個三角形了。又所有的邊不能相交,因此每連接一條邊,就相當於把整個平面上的點劃分成了兩個部分,在接下來的遊戲中,只能單獨在兩部分裏面進行,相當於將一個遊戲劃分成了兩個遊戲。因此當前狀態x的sg函數值就是兩個子游戲的Nim和了。

即:sg(x) = mex{ sg(i)^sg(x-i-2) }


這樣複雜度很高。但是這道題目有規律,在大數據的範圍下,會出現循環,循環長度爲34,因此只需要小數據暴力,大數據打表就哦了。


#include <cstdio>
#include <cstring>
typedef long long ll;
#define N 1002
bool vis[N];
int sg[N];
int a[] = {4,8,1,1,2,0,3,1,1,0,3,3,2,2,4,4,5,5,9,3,3,0,1,1,3,0,2,1,1,0,4,5,3,7};
int SG(int x) {
    if (sg[x] != -1) return sg[x];
    if (x == 0) return 0;
    if (x == 1) return 0;
    if (x == 2) return 1;
    if (x == 3) return 1;
    memset(vis, false, sizeof(vis));
    for (int i=0; i<x-1; i++) vis[SG(i)^SG(x-i-2)] = true;
    for (int i=0; ;i++) if (!vis[i]) return i;
}
int get_sg(int x) {
    if (x <= 100) return sg[x];
    return a[x%34];
}
int main() {
    memset(sg, -1, sizeof(sg));
    for (int i=0; i<=100; i++) sg[i] = SG(i);

    int T, n, x; scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        int ans = 0;
        while (n--) { scanf("%d", &x); ans ^= get_sg(x); }
        puts(ans ? "Carol" : "Dave");
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章