字典树+博弈 CF 455B A Lot of Games(接龙游戏)

题目链接

题意:

  A和B轮流在建造一个字,每次添加一个字符,要求是给定的n个串的某一个的前缀,不能添加字符的人输掉游戏,输掉的人先手下一轮的游戏。问A先手,经过k轮游戏,最后胜利的人是谁。

思路:

  很显然先将n个字符串插入到字典树上,因为字典树上有分叉,不能仅仅判断字符串长度奇偶性来判断。字典树看成无环的状态图,如果按照拓扑排序逆序进行排序,在判断每个节点的时候,它的后继都已经判断过了。定义两个数组can_win[u], can_lose[u],表示u节点走下去“可能赢吗?”以及u节点走下去”可能输吗?“,那么取反就是必胜和必败态了,“可能赢”<-对手下一步不可能赢,”可能输“<-对手下一步不可能输。

代码:

#include <bits/stdc++.h>

const int N = 1e5 + 5;
char str[N];
int ch[N][26];
int sz;

bool can_win[N], can_lose[N];

void init() {
    memset (ch[0], 0, sizeof (ch[0]));
    sz = 1;
}

void insert(char *s) {
    int u = 0, c;
    for (int i=0; s[i]; ++i) {
        c = s[i] - 'a';
        if (!ch[u][c]) {
            memset (ch[sz], 0, sizeof (ch[sz]));
            ch[u][c] = sz++;
        }
        u = ch[u][c];
    }
}

void DFS(int u) {
    can_win[u] = can_lose[u] = false;
    bool is_leaf = true;
    for (int c=0; c<26; ++c) {
        int v = ch[u][c];
        if (v) {
            is_leaf = false;
            DFS (v);
            can_win[u] |= !can_win[v];
            can_lose[u] |= !can_lose[v];
        }
    }
    if (is_leaf) {
        can_lose[u] = true;
    }
}

int main() {
    int n, k;
    scanf ("%d%d", &n, &k);
    init ();
    for (int i=0; i<n; ++i) {
        scanf ("%s", str);
        insert (str);
    }
    
    DFS (0);

    if (!can_win[0]) {
        puts ("Second");  //of course!
    } else if (can_lose[0]) {
        puts ("First");  //can lose && can win, control!
    } else {
        //can win && !can lose, change A and B every turn!
        printf ("%s
", (k & 1) ? "First" : "Second");
    }
    return 0;    
}

  

原文地址:https://www.cnblogs.com/Running-Time/p/5674693.html