如果您愿意转载,请注明出处不谢
TLE君的强连通日记
Nero的强连通专辑2
HDU1269 迷宫城堡
判断是不是强连通图。数据超级水,事实上随便写个dfs就能过了
HDU2767 Proving Equivalences
添加最少数量的有向边把原图变成强连通图
这道题靠dfs果然已经水不过去了,于是只好老老实实学了一下tarjan (其实tarjan的精髓就是如何用栈?)
先tarjan把强连通分量缩点,得到有向无环图,设有a个点没有出度(方便起见称为叶子),b个点没有入度(根)
则答案就是max(a,b)
假设a > b
首先呢,至少需要a条边,才有能让这a+b个点连通,
然后,从每个叶子引出一条边连向第一片叶子不能到达的叶子的根,最多用a-1条边,就可以让第一片叶子能够到达其他所有的叶子,
最后,把剩下的没有连出的叶子连向第一片叶子,就变成回路了
a < b的情况也类似
by the way,感觉强连通tarjan算法就是dfs树+栈,代码不长但是却处理了有向图的所有情况
HDU3836 Equivalent Sets
和上一题是一个东西,只是换了一个背景
HDU1827 Summer Holiday
点有权,选出最少的点及最小的点权和,使得所有的点都能够被被选点通过有向边遍历到
scc缩个点先,得到新的有向无环图,新图的每个点的点权为对应原强连通分量中的最小点权
然后,最少点数就是新图入度为0的点的数量,最小点权和就是新图入度为0的点的权和
HDU3072 Intelligence System
选择最小权边集,使得所有的点都能够被从0遍历(强连通分量费用为0)
显然先scc缩个点,新图的点权为原图连向该强连通分量的最小边权,0所在的强连通分量权值为0
答案就是新图的点权和
HDU3861 The King’s Problem
其实就是用最少的不相交有向路径覆盖强连通分量缩点后的所有的点
感觉tarjan在这里简直就是个模板,然后缩完点就没他什么事了= =
scc缩点,然后dfs遍历新图,当前节点与随便一个子节点共用一条路径,其他子节点则新开路径 (没什么最优性质,把父节点尽量往下连就好了。。)
然后输出答案
HDU3639 Hawk-and-Chicken
缩点,新图的点权为对应强连通分量内的点数量,
然后问题就变成怎么统计有向无环图的每个子树节点权和
这个。。暴力dfs就过去了= =
忘记是谁说的了,无可奈何时用暴力,大胆暴力出奇迹,暴力才是真爱啊!!
HDU3594 Cactus
判断原图是不是有向仙人掌
有向图仙人掌:
1:强连通
2:每条边属于且只属于一个环
我先睡了一觉,然后起来先用tarjan判完强连通,然后yy了一个看起来很对的dfs瞬间就过去了。。
然后AC代码被某巨巨的神数据叉出了翔,然后又各种打补丁,现在看起来大概是木有问题了- -b
真尼玛欢乐的题目啊
附代码,有兴趣的童鞋帮我再叉一叉,有神数据请留言谢谢。。
#include
#include
#include
#include
using namespace std;
#define snuke(it,x) for (__typeof((x).begin()) it = (x).begin(); \
it != (x).end(); it ++)
const int N = 22222;
int stack[N],instack[N],top,tot,tim,dfn[N],low[N],n,vis[N],dep[N];
vector G[N];
bool dfs(int u,int deep) {
vis[u] = 1;
dep[u] = deep;
snuke(it,G[u]) {
if (!instack[u]) {
instack[u] = 1;
stack[top++] = u;
}
int v = *it;
if (!vis[v]) {
if (!dfs(v,deep+1)) return false;
} else if (instack[v]) {
int pre = dep[u];
while (true) {
int c = stack[--top];
if (dep[c]!=pre) return false;
pre --;
instack[c] = 0;
if (c==v) break;
}
} else {
return false;
}
}
return true;
}
void tarjan(int u) {
dfn[u] = low[u] = ++tim;
stack[top++] = u;
instack[u] = 1;
snuke(it,G[u]) {
int v = *it;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u],low[v]);
} else if (instack[v]) {
low[u] = min(low[u],low[v]);
}
}
if (dfn[u]==low[u]) {
while (true) {
int c = stack[--top];
instack[c] = 0;
if (c==u) break;
}
tot ++;
}
}
void scc() {
tot = top = tim = 0;
memset(dfn,0,sizeof(dfn));
memset(instack,0,sizeof(instack));
for (int i = 0; i < n; i ++) if (!dfn[i]) tarjan(i);
}
int main() {
int cas;
scanf("%d",&cas);
while (cas--) {
scanf("%d",&n);
for (int i = 0; i < n; i ++) {
G[i].clear();
}
int a,b;
while (scanf("%d%d",&a,&b)==2 && (a||b)) {
G[a].push_back(b);
}
if (n==1) {
puts("YES");
continue;
}
scc();
if (tot>1) {
puts("NO");
continue;
}
top = 0;
memset(instack,0,sizeof(instack));
memset(vis,0,sizeof(vis));
bool ans = dfs(0,0);
puts(ans ? "YES" : "NO");
}
return 0;
}
POJ1236 Network of Schools
tarjan缩点后得到新图
taskA是问新图中无入度的点有多少
taskB是问max(新图中无入度的点数,新图中无出度的点数)
注意特判只有1个强连通分量的情况
POJ2553 The Bottom of a Graph
tarjan缩点后,位于没有出度的强连通分量内的点就是答案
POJ2186 Popular Cows
强连通分量缩点
如果新图有大于1个叶子,则无答案
否则答案为位于叶子连通分量内的点的数量
POJ2375 Cow Ski Area
问最少要几条边让原图变成强连通图,老题目了,min(根强连通分量数,叶子强连通分量数)
交C++防RE
POJ2762 Going from u to v or from v to u?
问原图是不是满足对于任意点对u和v,都满足u能到v或者v能到u
强连通分量缩点后,check存不存在一条最长链覆盖所有点即可
POJ3160 Father Christmas flymouse
强连通分量缩点后,dfs找一条权和最大路径即可
注意有负权点,直接无视负点即可
POJ3180 The Cow Prom
卧槽这什么题面啊。。百度了才知道要干嘛- -b
就是问点数大于等于2的强连通分量的数量
POJ3114 Countries in War
100次询问u是不是能走到v
强连通分量缩点后每次从belong[u] 做一次bfs即可(突然觉得缩点过程可有可无。。就当优化好了- -b)
POJ3592 Instantaneous Transference
强连通分量缩点然后找最大权和路径。
注意貌似有传送到'#'的数据,我在这里WA了两次。
POJ1904 King's Quest
大概这是这套题里唯一需要一点脑子的题了。。
题面给出了一幅二分图和一组完美匹配,问每个左点可以和哪些右点连而不会破坏完美匹配
要做这题呢,大概你首先得会匈牙利求二分图最大匹配
设王子u原先和公主v匹配,但是王子u现在想和公主w匹配
那么必须要满足存在这样一条增广路:起点是u,第二个点是w,终点是v
而v有到达u的边(u和v是最初给定的匹配)
也就是说,u,w,v三点在一个环上
而u,v本来就在一个环上
所以公主w能和u匹配且不破坏完美匹配的充分必要条件是w和u在一个环上(必要性显然)
所以建图就出来了:
对于王子u喜欢的公主v,连一条u->v
对于巫师list里的匹配u和v,连一条v->u
然后强连通分量缩点
对于王子u,公主v,若u喜欢v且u,v在一个强连通分量里,则u可以选v
附代码
#include
#include
#include
#include
using namespace std;
#define snuke(it,x) for (vector::iterator it = (x).begin();\
it != (x).end(); it ++)
const int N = 2222<<1;
int dfn[N],low[N],belong[N],stack[N],instack[N],tim,top,tot,n,m;
vector G[N];
void tarjan(int u) {
dfn[u] = low[u] = ++tim;
stack[top++] = u;
instack[u] = 1;
snuke(it,G[u]) {
int v = *it;
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]) {
while (true) {
int v = stack[--top];
instack[v] = 0;
belong[v] = tot;
if (v==u) break;
}
tot ++;
}
}
void scc() {
top = tim = tot = 0;
memset(dfn,0,sizeof(dfn));
memset(instack,0,sizeof(instack));
for (int i = 0; i < n<<1; i ++) if (!dfn[i]) tarjan(i);
}
int main() {
while (scanf("%d",&n)==1) {
for (int i = 0; i < n<<1; i ++) {
G[i].clear();
}
for (int i = 0; i < n; i ++) {
int d,x;
scanf("%d",&d);
while (d--) {
scanf("%d",&x); x --;
G[i].push_back(x+n);
}
}
for (int i = 0; i < n; i ++) {
int x;
scanf("%d",&x); x --;
G[x+n].push_back(i);
}
scc();
for (int i = 0; i < n; i ++) {
vector ans;
snuke(it,G[i]) {
int v = *it;
if (belong[i]==belong[v]) {
ans.push_back(v-n);
}
}
sort(ans.begin(),ans.end());
printf("%d",(int)ans.size());
snuke(it,ans) {
int v = *it;
printf(" %d",v+1);
}
puts("");
}
}
return 0;
}