2 4 3 3 2 2 0 2 1 3 3 1 0 2 1 0 2
Case 1: 2 0 1 Case 2: 2 0 1 2
题意:现在有n个孩子正在玩游戏,完成一个传递的游戏,举个例子,A传递到B小朋友,然后B小朋友传递给C小朋友,那么此时,C小朋友将拥有2的传递值,C是胜利者,若C又传递回A,那么A,B,C三者都拥有传递值为2的胜利者。问胜利着获得的最大传递值,以及胜利者的编号(可能有多个)。
解析:这题的 n的数据量为5000, 暴力肯定是不行的,有题意可知:如有 A - > B, B - > C, C - >A,那么A,B,C都是拥有传递值为2的胜利者。所以我们可以先缩点求出SCC,对每个SCC内的点来说, 它们的传递值是一样的,但要明白:如果从一个SCC:a, 传到另一个SCC:b, 即 a - > b,传递的值为SCC a 内部点的个数,这点要清楚
而且我们知道,胜利者肯定在那些出度为0的SCC中,所以我们可以从每个入度为0的SCC开始搜索,DFS遍历一遍,得到每个出度为0的SCC的传递值,找出最大的即可,
但这种思路是不对的,因为入度为0的SCC的个数肯定远远的大于出度为0的SCC个数,而且这题的n是非常大的。枚举每个入度为0的SCC然后DFS遍历一遍肯定会超时。
所以我们要反向建图, 这样每次从入度为0的SCC【即正向建边出度为0 的SCC】开始DFS,就可以得到每个入度为0的SCC的传递值。找出其中传递值最大的。这里还有注意
当我们找这个传递值最大的SCC :A 时,我们把这SCC的传递值多算了1,因为我们算的时候是累加的每个SCC中点的个数。当找到传递值最大的SCC A时,它内部的传递值为 内部点的个数 - 1。(如A - > B, B - > C, C - >A,传递值为2),但我们加的时候没有 - 1,所以最后的结果 - 1才是正确答案,这点比较绕,我解释的也不是太清楚,读者自己多想想,不是太难明白。
明天开始刷独立集,2-SAT, 强连通就先做到这,。
这应该是有史以来自己写的最长的解析了,以前直接总是懒得写题解,觉得能写出AC代码就行了,有时懒省事就粘一下别人的。但现在才发现自己写解析的时候收获才最大的,以前真真真真真蠢。能把自己的思路完整的用文字呈现出来,肯定是比较理解解题的思路了。以后解析都要自己写,还要好好写。加油阿欢。
<pre name="code" class="cpp">#include <cstdio> #include <cstring> #include <vector> #include <algorithm> #define maxn 5000 + 500 #define maxm 30000 + 3000 using namespace std; struct node{ int u, v, next; }; node edge[maxm]; int head[maxn], cnt; int low[maxn], dfn[maxn]; int dfs_clock; int Belong[maxn]; int scc_clock; bool Instack[maxn]; int Stack[maxn], top; bool vis[maxn];//记录缩点后的SCC是否访问 int in[maxn];//记录SCC的入度 int num[maxn];//记录每个SCC能得到的最大手帕数 vector<int>scc[maxn];//存储每个SCC中的节点 vector<int> Map[maxn];//存储缩点后新图 int n, m, k; void init(){ cnt = 0; memset(head, -1, sizeof(head)); } void add(int u, int v){ edge[cnt] = {u, v, head[u]}; head[u] = cnt++; } void getmap(){ scanf("%d%d", &n, &m); int a, b; while(m--){ scanf("%d%d", &a, &b); a++, b++; add(a, b); } } void tarjan(int u){ int v; low[u] = dfn[u] = ++dfs_clock; Stack[top++] = u; Instack[u] = true; for(int i = head[u]; i != -1; i = edge[i].next){ v = edge[i].v; if(!dfn[v]){ tarjan(v); low[u] = min(low[u], low[v]); } else if(Instack[v]) low[u] = min(low[u], dfn[v]); } if(low[u] == dfn[u]){ scc_clock++; scc[scc_clock].clear(); do{ v = Stack[--top]; Instack[v] = false; Belong[v] = scc_clock; scc[scc_clock].push_back(v); }while(u != v); } } void find(){ memset(low, 0, sizeof(low)); memset(dfn, 0, sizeof(dfn)); memset(Belong, 0, sizeof(Belong)); memset(Stack, 0, sizeof(Stack)); memset(Instack, false, sizeof(Instack)); dfs_clock = scc_clock = top = 0; for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(i); } void suodian(){//缩点新建图 for(int i = 1; i <= scc_clock; ++i){ Map[i].clear(); in[i] = 0; } for(int i = 0; i < cnt; ++i){ int u = Belong[edge[i].u]; int v = Belong[edge[i].v]; if(u != v){ Map[v].push_back(u);//反向建边 in[u]++; } } } int ans; void DFS(int u){ vis[u] = true; ans += scc[u].size(); for(int i = 0; i < Map[u].size(); ++i){ int v = Map[u][i]; if(!vis[v]){ DFS(v); } } } void solve(){ int sum = -1; for(int i = 1; i <= scc_clock; ++i){ num[i] = 0; if(in[i] == 0){ memset(vis, false, sizeof(vis)); ans = 0; DFS(i); num[i] = ans; sum = max(num[i], sum); } } printf("Case %d: %d\n", k++, sum - 1);//多算1,要减去1 int flag = 0; for(int i = 1; i <= n; ++i){ if(num[Belong[i]] == sum){ if(!flag) printf("%d", i - 1); else printf(" %d", i - 1); flag = 1; } } printf("\n"); } int main(){ int T; scanf("%d", &T); k = 1; while(T--){ init(); getmap(); find(); suodian(); solve(); } return 0; }