AtCoder Beginner Contest 209 E - Shiritori「bfs版对抗博弈」

E - Shiritori

题目描述:

成语接龙,有n个长度大于等于3的字符串,当前说到s,下次说的字符串的前3个字符必须和s的后三个完全相同

Takahashi先手,Aoki后手,二者轮流说成语,谁先不能说出成语则输,每个串都可以使用任意多次

问以第i个字符串为开头的时候,谁会赢,如果陷入循环,则输出Draw

思路:

又是一个对抗博弈的题目

n 2 n^2 n2建图肯定不可取,我们发现,只存在大小写的字母,且只有三位有用,所以可以对每个串,哈希出前三位和后三位,得到uv,建u->v就行

考虑状态,以点u为起点时,如果先手必输,则ans[u]=0,如果先手必赢,则ans[u]=1

  • 如果一个点没有任何的出边,那这个点就是先手必输,即不存在串使的该点能转移到别的点
  • 如果一个点一步只能走到先手必胜点,则他先手必输
  • 如果一个点可以一步走到一个先手必输点,则他先手必赢
  • 如果这个点既不是先手必胜点和不是先手必输点,则是平局点

对于一般来说的对抗博弈题,我们都是去跑dfs,但是这里存在可以循环的点,dfs不好判,所以改成bfs,改成bfs的时候就需要注意了,之前跑dfs的时候,我们是去递归跑完所有子节点,所以最先确定状态的其实所有出度为0的点,如果是博弈树的话,也就是所有叶子节点,所以改成bfs的时候,我们应该先确定叶子节点,也就是出度为0的点,也就是需要我们反向建图,对于出度为0的点,他们的状态就是先手必输,状态为0。遇到状态确定的点就跳过,否则,我们就给出度减1,如果当前点u是先手必输,那对于u的子节点v,他的状态就是先手必胜,即1,并给他塞队列里;如果v的出度已经归0,此时他还没有确实状态,则说明能到达他的点都是必胜点,则他就是必输点,赋状态为0,并给他塞到队列里

输出结果的时候,我们应该判断后三个字母对应的状态,因为以当前串为起始串时,我们先手的人一定会用这个串,而我们求的状态可没有这个规定,所以我们判后三个字母为起始串就可以

#include 
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod7 1000000007
#define mod9 998244353
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
int n, m, k, x;
int tr[MAX];
string s;

int getid(char x){
    if(x >= 'A' && x <= 'Z')return x - 'A';
    else return x - 'a' + 26;
}

int calc(char a, char b, char c){
    return getid(a) * 52 * 52 + getid(b) * 52 + getid(c);
}
vector<int>G[MAX];
int du[MAX];
int ans[MAX];
void work(){
    cin >> n;
    for(int i = 1; i <= n; ++i){
        cin >> s;
        int a = calc(s[0], s[1], s[2]);
        int b = calc(s[(int)s.size() - 3], s[(int)s.size() - 2], s[(int)s.size() - 1]);
        G[b].push_back(a);
        ++du[a];
        tr[i] = b;
    }
    mem(ans, -1);
    queue<int>q;
    for(int i = 0; i < 52 * 52 * 52 + 5; ++i){
        if(!du[i]){
            ans[i] = 0;
            q.push(i);
        }
    }
    while (!q.empty()) {
        int u = q.front();q.pop();
        for(auto v : G[u]){
            if(ans[v] != -1)continue;
            --du[v];
            if(ans[u] == 0){
                ans[v] = 1;
                q.push(v);
            }
            else if(du[v] == 0){
                ans[v] = 0;
                q.push(v);
            }
        }
    }
    for(int i = 1; i <= n; ++i){
        int p = ans[tr[i]];
        if(p == 0)cout << "Takahashi\n";
        else if(p == 1)cout << "Aoki\n";
        else cout << "Draw\n";
    }
}


int main(){
    io;
    work();
    return 0;
}

你可能感兴趣的:(Atcoder)