AtCoder Beginner Contest 278
《F - Shiritori 》
博弈
首先在这个博弈题中有个很重要的结论:
1.如果一个点,走一步,能够到达的点如果其中有一个为先手必胜点,那么这个点必然是先手必败点
2.如果一个点,走一步,能够到达的点如果全部为先手必败点,那么这个点必然是先手必胜点
这道题如果用爆搜的方式:
首先我们可以根据他的单词首尾相连的方式建个图,这个建图很有讲究,可以在O(n)的时间内建出
即就是将开头为c的单词的下标i,保存在集合中,然后
在尾部为单词needc时,就去找头部为needc的单词
注意这里的爆搜是要回溯的,因为全局的状态会根据搜索的起始点儿不同,比如:
于是:
1 #include <iostream>
2 #include <algorithm>
3 #include <cstring>
4 #include <vector>
5 using namespace std;
6 const int N = 20;
7 // dp[i]表示以第i个字符串为先手最终的结果
8 //-1表示最初始的状态,1表示先手必胜,0表示先手必败
9 int dp[N], n;
10 string str[N];
11 // sides[i]表示以字母i开头的字符有哪些
12 vector<int> sides[2 * N];
13 // dfs时有用
14 bool vis[N];
15 int dfs(int u)
16 {
17 int needc = str[u].back() - 'a';
18 for (int i = 0; i < sides[needc].size(); i++)
19 {
20 int to = sides[needc][i];
21 if (!vis[to])
22 {
23 vis[to] = true;
24 if (dfs(to))
25 {
26 vis[to] = false;
27 return dp[u] = 0;
28 }
29 vis[to] = false;
30 }
31 }
32 return dp[u] = 1;
33 }
34 int main()
35 {
36 cin >> n;
37 for (int i = 1; i <= n; i++)
38 {
39 cin >> str[i];
40 int c = str[i][0] - 'a';
41 sides[c].push_back(i);
42 }
43 for (int i = 1; i <= n; i++)
44 {
45 vis[i] = true;
46 if (dfs(i))
47 {
48 cout << "First" << endl;
49 return 0;
50 }
51 vis[i] = false;
52 }
53 cout << "Second" << endl;
54 return 0;
55 }
然鹅会超时,按照状态压缩我还不理解:
这里我有很多疑惑点:
1.这里枚举k,如果枚举的最后的一个单词,根本不存在怎么办?
比如状态1111011是枚举到的,但是枚举到的单词k根据不存在于任何上面的状态单词中怎么办?
《AtCoder Beginner Contest 209 E - Shiritori 》
DAG(有向无环图),拓扑排序,博弈
参考博客:https://blog.csdn.net/weixin_51216553/article/details/124272620
1 #include <iostream>
2 #include <algorithm>
3 #include <cstring>
4 #include <vector>
5 #include <queue>
6 using namespace std;
7 const int N = 200005;
8 // backstr[i]表示以数字i结尾的字符串有..
9 vector<int> sides[N], backstr[N];
10 string str[N];
11 int n, rd[N];
12 queue<int> q;
13 int get(string str)
14 {
15 int num = 0;
16 for (int i = 0; i < 3; i++)
17 {
18 if ('A' <= str[i] && str[i] <= 'Z')
19 num += (str[i] - 'A' + 26);
20 else if ('a' <= str[i] && str[i] <= 'z')
21 num += (str[i] - 'a' + 0);
22 num *= 52;
23 }
24 num /= 52;
25 return num;
26 }
27 // ans[i]表示从第i个字符串开始的状态,如果为1表示必胜,-1表示必输,0表示平局
28 int ans[N];
29 void bfs()
30 {
31 while (q.size())
32 {
33 int top = q.front();
34 q.pop();
35 /* cout << "top:" << top << endl; */
36 int backnum = get(str[top].substr(0, 3));
37 // 这里可以保证backstr[backnum].size()>0,因为如果其不大于0,那么这个top字符串就不会匹配到他了
38 // 我这里看的是top字符串可以指向哪些字符串
39 for (int i = 0; i < backstr[backnum].size(); i++)
40 {
41 int to = backstr[backnum][i];
42 // 表示这个点的状态已经确定
43 if (ans[to] != 0)
44 continue;
45 rd[to]--;
46 // 表示存在一条可以让从to开始失败的路即to->top;
47 // 即to是必败点
48 //只要状态确定,就可以放到队列中更新状态
49 if (ans[top] == 1)
50 {
51 ans[to] = -1;
52 q.push(to);
53 }
54 // 表示到目前为为止周围的点都是必败点,同时rd[to]==0,表示把点都看过了
55 else if (ans[top] == 0 && rd[to] == 0)
56 {
57 ans[to] = 1;
58 q.push(to);
59 }
60 }
61 }
62 }
63 int main()
64 {
65 cin >> n;
66 for (int i = 1; i <= n; i++)
67 {
68 cin >> str[i];
69 sides[get(str[i].substr(0, 3))].push_back(i);
70 backstr[get(str[i].substr(str[i].length() - 3, 3))].push_back(i);
71 }
72 for (int i = 1; i <= n; i++)
73 {
74 /* cout << str[i].substr(str[i].length() - 3, 3) << endl; */
75 string backstr = str[i].substr(str[i].length() - 3, 3);
76 int num = get(backstr);
77 /* cout << num << endl; */
78 rd[i] += sides[num].size();
79 /* cout<<rd[i]<<endl; */
80 if (rd[i] == 0)
81 {
82 ans[i] = 1;
83 q.push(i);
84 }
85 }
86 bfs();
87 for (int i = 1; i <= n; i++)
88 {
89 if (ans[i] == 0)
90 cout << "Draw" << endl;
91 else if (ans[i] == 1)
92 cout << "Takahashi" << endl;
93 else
94 cout << "Aoki" << endl;
95 }
96 return 0;
97 }
但是这份代码是错误的。。。。。我完全不知道错在哪里