Kosaraju算法 O(n+m)

vector<int> s;

void dfs1(int u) {
vis[u] = true;
for (int v : g[u])
if (!vis[v])
dfs1(v);
s.push_back(u);
} void dfs2(int u) {
color[u] = sccCnt;
for (int v : g2[u])
if (!color[v])
dfs2(v);
} void Kosaraju() {
s.clear();
for (int i = 1; i <= n; ++i)
if (!vis[i])
dfs1(i);
sccCnt = 0;
for (int i = n; i >= 1; --i)
if (!color[s[i]]) {
++sccCnt;
dfs2(s[i])
}
}

https://www.luogu.org/problem/P1262

首先考虑假如一个间谍没办法被揭发不能被贿赂,也就是他不是可行的入口点也没有别人指向他,那么无解。

否则可能要若干个入度为0的点,这些点必须被贿赂,且这些点能到达的点不需要再贿赂。

否则一定存在环,或者多个环交在一起的,而这些环都是同一强连通分量内的,找这个强连通分量里面的最小的那个。

有个问题就是加入强连通分量里的都不能被贿赂就很尴尬了,所以干脆一开始就把强连通分量用编号最小的点来代替?

这个就是在dfs1的时候把额外的信息维护好了,然后在dfs2的时候维护这个强连通分量的最小花费以及假如是INF的话的最小id。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; const int MAXN = 3005;
const int INF = 0x3f3f3f3f; int n,w[MAXN], indeg[MAXN]; vector<int> G[MAXN], G2[MAXN]; //从i点出发的连通分量,染色为c1[i]
int c1[MAXN],cntc1;
bool visc1[MAXN]; //i点所在的强连通分量,染色为c2[i]
int c2[MAXN],cntc2;
int minCost[MAXN], minID[MAXN]; int s[MAXN],cnts; void dfs1(int u,int c) {
c1[u]=c;
for (int v : G[u])
if (!c1[v])
dfs1(v,c);
s[++cnts]=u;
} void dfs2(int u, int &minid) {
c2[u] = cntc2;
minCost[cntc2] = min(minCost[cntc2], w[u]);
minid = min(minid, minID[cntc2]);
for (int v : G2[u])
if (!c2[v])
dfs2(v, minid);
} void Kosaraju(ll &sum, int &minid) {
minid = INF;
for (int i = 1; i <= n; ++i)
if (!c1[i]){
++cntc1;
dfs1(i,cntc1);
}
for (int i = n; i >= 1; --i) {
if (!c2[s[i]]) {
++cntc2;
minCost[cntc2] = INF;
minID[cntc2] = INF;
dfs2(s[i], minid);
}
if(minCost[cntc2] == INF)
minid = min(minid, minID[cntc2]);
else{
if(!visc1[c1[s[i]]]){
visc1[c1[s[i]]]=true;
sum += minCost[cntc2];
}
}
}
} int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int p;
scanf("%d%d", &n, &p);
memset(w, INF, sizeof(w[0]) * (n + 1));
for(int i = 1; i <= p; ++i) {
int id, c;
scanf("%d%d", &id, &c);
w[id] = c;
}
int m;
scanf("%d", &m);
for(int i = 1; i <= m; ++i) {
int u, v;
scanf("%d%d", &u, &v);
++indeg[v];
G[u].emplace_back(v);
G2[v].emplace_back(u);
}
ll sum = 0;
for(int i = 1; i <= n; ++i) {
if(indeg[i] == 0 && w[i] == INF) {
puts("NO");
printf("%d\n", i);
return 0;
} else if(indeg[i] == 0) {
//处理了所有入度为0的点,剩下的必定是独立环
++cntc1;
dfs1(i,cntc1);
visc1[cntc1]=true;
sum += w[i];
}
}
int minid;
Kosaraju(sum, minid);
if(minid == INF) {
puts("YES");
printf("%lld\n", sum);
} else {
//某个强连通分量里有不能被贿赂的点
puts("NO");
printf("%d\n", minid);
}
return 0;
}

https://www.luogu.org/problem/P1262

首先每个入链必须都要给一套,然后剩下的必有环,或者环带出链。这样就很麻烦了。把整个图的强连通分量缩成点之后,变成若干独立的链(这些链可能会交叉)。入度为0的点就是子问题1的答案。子问题2里面,考虑每次多连一条边可以消除至多1个入度为0的和1个出度为0的(就算是孤立点,也是要连一条入边一条出边才能强连通),整个图强连通肯定存在一种首尾相连的办法。答案为两者间的最大值。特例是缩点之后假如只剩下一个点,那么不需要连边。

注意一定要先处理掉链,然后进去找强连通分量,成功缩点之后新图的G的边记得要去重,方便的话可以用set,怕卡就vector然后sortunique。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; const int MAXN = 105;
const int INF = 0x3f3f3f3f; int n, w[MAXN], indeg[MAXN]; vector<int> G[MAXN], G2[MAXN]; //从i点出发的连通分量,染色为c1[i]
int c1[MAXN], cntc1; //i点所在的强连通分量,染色为c2[i]
int c2[MAXN], cntc2; //第i个强连通分量内的点
vector<int> C2[MAXN]; int s[MAXN], cnts; void dfs1(int u) {
c1[u] = cntc1;
for (int v : G[u])
if (!c1[v])
dfs1(v);
s[++cnts] = u;
} void dfs2(int u) {
C2[cntc2].push_back(u);
c2[u] = cntc2;
for (int v : G2[u])
if (!c2[v])
dfs2(v);
} void Kosaraju() {
//再计算环
for (int i = 1; i <= n; ++i)
if (!c1[i]) {
++cntc1;
dfs1(i);
}
for (int i = n; i >= 1; --i)
if (!c2[s[i]]) {
++cntc2;
dfs2(s[i]);
}
} set<int> G3[MAXN];
int indeg3[MAXN], outdeg3[MAXN]; int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
scanf("%d", &n);
for(int u = 1; u <= n; ++u) {
int v;
while(1) {
scanf("%d", &v);
if(!v)
break;
G[u].push_back(v);
G2[v].push_back(u);
++indeg[v];
}
}
for (int i = 1; i <= n; ++i)
if (!c1[i] && indeg[i] == 0) {
++cntc1;
dfs1(i);
}
Kosaraju();
if(cntc2 == 1) {
//只有一个强连通分量
printf("%d\n%d\n", 1, 0);
} else {
for(int u = 1; u <= cntc2; ++u) {
for(auto ui : C2[u]) {
for(auto vi : G[ui]) {
if(c2[vi] != u) {
G3[u].insert(c2[vi]);
++indeg3[c2[vi]];
++outdeg3[u];
}
}
}
} /*for(int i = 1; i <= n; ++i) {
printf("%d:%d\n", i, c2[i]);
}
puts(""); for(int i=1;i<=cntc2;++i){
printf("u=%d\n",i);
for(auto v:G3[i])
printf(" v=%d\n",v);
}*/ int in0 = 0, out0 = 0;
for(int u = 1; u <= cntc2; ++u) {
if(indeg3[u] == 0)
++in0;
if(outdeg3[u] == 0)
++out0;
}
printf("%d\n%d\n", in0, max(in0, out0));
}
return 0;
}

模板 - 强连通分量 - Kosaraju的更多相关文章

  1. 模板 - 图论 - 强连通分量 - Kosaraju算法

    这个算法是自己实现的Kosaraju算法,附带一个缩点,其实缩点这个跟Kosaraju算法没有什么关系,应该其他的强连通分量算法计算出每个点所属的强连通分量之后也可以这样缩点. 算法复杂度: Kosa ...

  2. 强连通分量-----Kosaraju

    芝士: 有向图强连通分量在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connect ...

  3. POJ 2186 Popular Cows(强连通分量Kosaraju)

    http://poj.org/problem?id=2186 题意: 一个有向图,求出点的个数(任意点可达). 思路: Kosaraju算法的第一次dfs是后序遍历,而第二次遍历时遍历它的反向图,从标 ...

  4. 有向图的强连通分量——kosaraju算法

    一.前人种树 博客:Kosaraju算法解析: 求解图的强连通分量

  5. 图的强连通分量-Kosaraju算法

    输入一个有向图,计算每个节点所在强连通分量的编号,输出强连通分量的个数 #include<iostream> #include<cstring> #include<vec ...

  6. 模板 - 强连通分量/割点/桥 - Tarjan

    int dfn[N], low[N], dfncnt, s[N], tp; int scc[N], sc; // 结点 i 所在 scc 的编号 int sz[N]; // 强连通 i 的大小 voi ...

  7. 强连通分量Kosaraju

    #include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #i ...

  8. 有向图强连通分量的Tarjan算法和Kosaraju算法

    [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极 ...

  9. 图论-求有向图的强连通分量(Kosaraju算法)

    求有向图的强连通分量     Kosaraju算法可以求出有向图中的强连通分量个数,并且对分属于不同强连通分量的点进行标记. (1) 第一次对图G进行DFS遍历,并在遍历过程中,记录每一个点的退出顺序 ...

随机推荐

  1. HDU 3669 [Cross the Wall] DP斜率优化

    问题分析 首先,如果一个人的\(w\)和\(h\)均小于另一个人,那么这个人显然可以被省略.如果我们将剩下的人按\(w[i]\)递增排序,那么\(h[i]\)就是递减. 之后我们考虑DP. 我们设\( ...

  2. HUD 1166:敌兵布阵(线段树 or 树状数组)

    敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem Des ...

  3. flink⼿手动维护kafka偏移量量

    flink对接kafka,官方模式方式是自动维护偏移量 但并没有考虑到flink消费kafka过程中,如果出现进程中断后的事情! 如果此时,进程中段: 1:数据可能丢失 从获取了了数据,但是在执⾏行行 ...

  4. C++入门经典-例6.2-将二维数组进行行列对换

    1:一维数组的初始化有两种,一种是单个逐一赋值,一种是使用聚合方式赋值.聚合方式的例子如下: int a[3]={1,2,3}; int a[]={1,2,3};//编译器能够获得数组元素的个数 in ...

  5. ubuntu环境配置终极解答

    1. ubuntu中常用的5个配置文件 1)/etc/profile 2)/etc/environment 环境变量在这个文件中定义,可以用vim /etc/environment查看该文件内容 3) ...

  6. vue项目内嵌入到app input type=file 坑(文件上传插件)

    w问题描述: 我用vue-cli完成的一个移动端项目,内嵌到app当中,用原生的input type=file 来完成文件上传.在安卓下没有问题但是在苹果手机 上传第二次手机就会发生白屏 并无缘无故跳 ...

  7. 利用 IntelliJ IDEA 进行代码对比的方法

    Sometimes,我们会有这样的需求,即:想对比出两个不同版本代码的区别.如何实现? 第 1 种:如果我们是从 SVN 检出的项目,并且想比较本地代码与从 SVN 检出时的代码相比都有那些区别,可以 ...

  8. LeetCode 55. 跳跃游戏(Jump Game)

    题目描述 给定一个非负整数数组,你最初位于数组的第一个位置. 数组中的每个元素代表你在该位置可以跳跃的最大长度. 判断你是否能够到达最后一个位置. 示例 1: 输入: [2,3,1,1,4] 输出: ...

  9. 二、linux下apache2.2.11+php5.6.3的环境配置

    参考文献:http://www.92csz.com/study/linux/16.htm 本节部分操作会直接使用上一节(http://www.cnblogs.com/yxpblog/p/4627509 ...

  10. 5.Python使用模块

    1.模块的 作用 2.模块的含义 3.模块的 导入  因此模块能够划分系统命名空间,避免了不同文件的变量重名的问题.                 Python的模块使得独立的文件连接成了一个巨大 ...