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 }

但是这份代码是错误的。。。。。我完全不知道错在哪里

 

 

 

 

 

posted @ 2022-11-24 11:06  次林梦叶  阅读(45)  评论(0)    收藏  举报