题目

大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?

Input
本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0 <m <= 30000),接下来有 M 行包含两个整数 A 和 B(A != B) 表示 A 认为 B 合适。

Output
对于每组数据,第一行输出 “Case x: ”,x 表示数据的编号,从1开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格!

Sample Input
2
4 3
3 2
2 0
2 1
3 3
1 0
2 1
0 2

Sample Output
Case 1: 2
0 1
Case 2: 2
0 1 2

思路

一、强连通分量(SCC)
强连通分量的概念:

来源:刘建东学长、黄瑞哲学长的ppt
二、kosaraju算法
kosaraju算法是一种求强连通分量的算法。
1.原图和反图

来源:刘建东学长、黄瑞哲学长的ppt
2.kosaraju算法
(1)用DFS在原图找逆后序序列
(2)用DFS在反图中按照逆后序序列遍历,每次由起点遍历到的点即构成一个强连通分量
三、解题
1.构建图
如果A认为B合适,则构建一条从A到B的边(A, B)。
2.求出图的强连通分量
使用kosaraju算法求出图的强连通分量。
3.缩点、求解
SCC[i]:第i个强连通分量中的点的数目
若有一条从第j个强连通分量到第i个强连通分量的边(即第j个强连通分量可达第i个强连通分量),则说明第j个“团体”的同学认为第i个“团体”的同学合适。
故设X在第i个强连通分量中,则认为X合适人数有:
SCC[i] - 1 + sum(SCC[j]),第j个强连通分量可达第i个强连通分量
不难理解,得票最多的同学一定在出度为0的强连通分量中。
故在反图中对每个入度为0的点进行DFS,计算其能到达的点的SUM(SCC[j]),即可得到答案。

代码

#include <iostream>
#include <vector>
#include <cstring>
#include <set>#define MAX_V 5050using namespace std;int T;
int N, M;
int A, B;vector<int> G1[MAX_V];   // 原图
vector<int> G2[MAX_V];    // 反图
vector<int> SCC[MAX_V];   // 缩点后的图int dfn[MAX_V]; // 逆后序序列
int vis[MAX_V];
int c[MAX_V];   // c[i]:第i个顶点所在的连通分量(的编号)int dcnt; // 逆后序序列计数
int scnt;   // 连通分量计数int vNum[MAX_V];  // vNum[i]:第i个连通分量中的顶点数int Max;   // 记录最大的点数
int cnt;    vector<int> v;
set<int> s[MAX_V];void dfs1(int x) { // 在原图找逆后序序列vis[x] = 1;for (int i = 0; i < G1[x].size(); i++)if (!vis[G1[x][i]])  dfs1(G1[x][i]);dfn[++dcnt] = x;
}void dfs2(int x) { // 在反图中按照逆后序序列遍历c[x] = scnt;vNum[scnt]++;for (int i = 0; i < G2[x].size(); i++) {if (!c[G2[x][i]]) dfs2(G2[x][i]);}
}void kosaraju()
{for (int i = 0; i < N; i++)if (!vis[i]) dfs1(i);for (int i = N - 1; i >= 0; i--)if (c[dfn[i]] == 0) {scnt++;dfs2(dfn[i]);}
}void shrink()
{for (int i = 0; i < N; i++)for (int j = 0; j < G1[i].size(); j++)if (c[i] != c[G1[i][j]]) {SCC[c[i]].push_back(c[G1[i][j]]);s[c[G1[i][j]]].insert(c[i]);}
}void dfs(int x) {vis[x] = 1;cnt += vNum[x];for (set<int>::iterator it = s[x].begin(); it != s[x].end(); it++)if (!vis[*it]) dfs(*it);
}//void print()
//{//  for (int i = 1; i <= scnt; i++) {//      if (!SCC[i].size()) {//          cnt = 0;
//
//          memset(vis, 0, sizeof(vis));
//
//          dfs(i);
//
//          if (cnt > Max) {//              Max = cnt;
//              v.clear();
//              v.push_back(i);
//          }
//          else if (cnt == Max)
//              v.push_back(i);
//      }
//  }
//
//  Max--;
//  cout << Max << endl;
//
//  bool end = false;
//  for (int j = 0; j < N; j++)
//      for (int k = 0; k < v.size(); k++)
//          if (c[j] == v[k]) {//              if (!end) end = true;
//              else cout << " ";
//              cout << j;
//          }
//
//  cout << endl;
//}void init()
{memset(dfn, 0, sizeof(dfn));memset(vis, 0, sizeof(vis));memset(c, 0, sizeof(c));memset(vNum, 0, sizeof(vNum));v.clear();for (int i = 0; i < N; i++) {G1[i].clear();G2[i].clear();SCC[i].clear();s[i].clear();}scnt = 0;dcnt = 0;Max = 0;
}int main() {cin >> T;for (int t = 1; t <= T; t++) {cin >> N >> M;init();for (int i = 0; i < M; i++) {cin >> A >> B;G1[A].push_back(B);G2[B].push_back(A);}kosaraju();shrink();cout << "Case " << t << ":" << " ";for (int i = 1; i <= scnt; i++) {if (!SCC[i].size()) {cnt = 0;memset(vis, 0, sizeof(vis));dfs(i);if (cnt > Max) {Max = cnt;v.clear();v.push_back(i);}else if (cnt == Max)v.push_back(i);}}Max--;cout << Max << endl;bool end = false;for (int j = 0; j < N; j++)for (int k = 0; k < v.size(); k++)if (c[j] == v[k]) {if (!end) end = true;else cout << " ";cout << j;}cout << endl;}return 0;
}

程序设计思维 C - 班长竞选 (强连通分量、kosaraju算法)相关推荐

  1. HDU4635(强连通分量+Kosaraju算法)

    题意:给出一个有向图,最多添加多少条边使这个图依然不是强连通图:当这个图是强连通图时,输出-1: 求解思路:强连通分量求解: 强连通图:在有向图中,任意节点除法都可以到达其余所有节点,则称为强连通图. ...

  2. HDU2767(强连通分量+Kosaraju算法)

    题意:需要加多少边才能把一个图变成强连通分量 强连通图:在有向图中,任意节点除法都可以到达其余所有节点,则称为强连通图. 强连通分量:在非强连通图的有向图中,选取部分点为强连通图,该强连通子图称为强连 ...

  3. 打印有向图的强连通分量-----kosaraju算法(最简单的实现)

    一.kosaraju算法步骤: 1.首先对图G进行一次DFS,记录每个顶点完成的顺序(DFS树/林的叶子节点先完成,然后回溯到它双亲这一层,它个双亲递归遍历完自己的邻居并在这些递归完成回溯到这层后,它 ...

  4. 强连通分量 Kosaraju PK Tarjan(转)

    转自scameeling的空间 http://hi.baidu.com/scameeling/item/b135831094ec756771d5e815 强连通分量 Kosaraju PK Tarja ...

  5. 有向图强连通分量tarjan算法

    转自:http://www.byvoid.com/blog/scc-tarjan/ http://blog.csdn.net/geniusluzh/article/details/6601514 在有 ...

  6. 强连通分量(Tarjan算法)和缩点

    强连通分量(Tarjan算法)和缩点 一些定义 给定一张有向图,对于图中任意两个节点 xxx 和 yyy ,存在从 xxx 到 yyy 的路径,也存在从 yyy 到 xxx 的路径,则称该有向图为强连 ...

  7. 强连通分量(Tarjan算法) 图解

    强连通分量(Tarjan算法) 前言 第一件事:没事不要while(m–),会带来不幸 第二件事:看博客先看看评论,如果博主他写错了的话- 简介 先讲几个定义 强连通:两个顶点 uuu,vvv 可以相 ...

  8. 图之强连通、强连通图、强连通分量 Tarjan算法

    一.解释 在有向图G中,如果两个顶点间至少存在一条互相可达路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强 ...

  9. 有向图的强连通分量--Tarjan算法---代码分析

    本文就是做个代码分析,顺便说下理解. 一.预备知识: 需要知道什么是: 回边.前向边.交叉边 二.上代码: #include<algorithm> #define NIL -1using ...

最新文章

  1. linux 安装java10
  2. 目标文件中的几个重要的段
  3. 《Windows via C/C++》学习笔记 —— Windows 线程池
  4. smartform连续打印,并自动补充空行
  5. [软件工程基础]结对项目 数独程序扩展
  6. hibernate连接数据库配置
  7. python创建空字典_Python创建和访问字典
  8. Airtable无法上传附件/图片;点击附件上传没反应;不安全的地址怎么设置为受信任;解决方法亲测有效
  9. OpenCV之图像的混合
  10. sql server 群集_部署SQL Server以使用群集共享卷进行故障转移群集–第1部分
  11. java jdbc 占位符_java-jdbc
  12. 扔掉JUnit,迎接Spock
  13. electron 自定义标题栏_CAXA电子图板2015版自定义标题栏
  14. 诱导系统服务器,交通诱导及信息发布系统
  15. 文件夹批量重命名编号的方法
  16. 如何解决Adobe Flash Player已被屏蔽
  17. snaker工作流核心表学习
  18. web技术_使用浏览器进行全双工通信的 WebSocketHTTP协议的性能瓶颈AjaxCometSPDY技术
  19. 给力的国外地质网站在线查看
  20. break语句的作用

热门文章

  1. 7 125 kHz RFID技术
  2. 华为手机摄影从入门到精通_华为手机拍照技巧从入门到精通
  3. html移动轮播后盾网,后盾网lavarel视频项目---Vue项目使用vue-awesome-swiper轮播插件...
  4. extjs给panel添加滚动条_ExtJs Panel 滚动条设置
  5. 联发科p60和骁龙710哪个好_骁龙670、联发科P60和麒麟710哪个好 详细对比介绍
  6. 多重背包问题和“二进制拆分”
  7. OpenCV局部阙值分割的自适应阙值算法
  8. EasyExcel代码层面设置写出的Excel样式、以及拦截器策略的使用、自动列宽设置、EasyExcel默认设置详解
  9. OPENSTREETMAP电力数据的情况
  10. 根据当前日期获取本周、本月、本季度相关日期