判斷是不是強連通圖。數據超級水,事實上隨便寫個dfs就能過了
添加最少數量的有向邊把原圖變成強連通圖
這道題靠dfs果然已經水不過去了,於是只好老老實實學了一下tarjan (其實tarjan的精髓就是如何用棧?)
先tarjan把強連通分量縮點,得到有向無環圖,設有a個點沒有出度(方便起見稱爲葉子),b個點沒有入度(根)
則答案就是max(a,b)
假設a > b
首先呢,至少需要a條邊,纔有能讓這a+b個點連通,
然後,從每個葉子引出一條邊連向第一片葉子不能到達的葉子的根,最多用a-1條邊,就可以讓第一片葉子能夠到達其他所有的葉子,
最後,把剩下的沒有連出的葉子連向第一片葉子,就變成迴路了
a < b的情況也類似
by the way,感覺強連通tarjan算法就是dfs樹+棧,代碼不長但是卻處理了有向圖的所有情況
和上一題是一個東西,只是換了一個背景
點有權,選出最少的點及最小的點權和,使得所有的點都能夠被被選點通過有向邊遍歷到
scc縮個點先,得到新的有向無環圖,新圖的每個點的點權爲對應原強連通分量中的最小點權
然後,最少點數就是新圖入度爲0的點的數量,最小點權和就是新圖入度爲0的點的權和
選擇最小權邊集,使得所有的點都能夠被從0遍歷(強連通分量費用爲0)
顯然先scc縮個點,新圖的點權爲原圖連向該強連通分量的最小邊權,0所在的強連通分量權值爲0
答案就是新圖的點權和
其實就是用最少的不相交有向路徑覆蓋強連通分量縮點後的所有的點
感覺tarjan在這裏簡直就是個模板,然後縮完點就沒他什麼事了= =
scc縮點,然後dfs遍歷新圖,當前節點與隨便一個子節點共用一條路徑,其他子節點則新開路徑 (沒什麼最優性質,把父節點儘量往下連就好了。。)
然後輸出答案
縮點,新圖的點權爲對應強連通分量內的點數量,
然後問題就變成怎麼統計有向無環圖的每個子樹節點權和
這個。。暴力dfs就過去了= =
忘記是誰說的了,無可奈何時用暴力,大膽暴力出奇跡,暴力纔是真愛啊!!
判斷原圖是不是有向仙人掌
有向圖仙人掌:
1:強連通
2:每條邊屬於且只屬於一個環
我先睡了一覺,然後起來先用tarjan判完強連通,然後yy了一個看起來很對的dfs瞬間就過去了。。
然後AC代碼被某巨巨的神數據叉出了翔,然後又各種打補丁,現在看起來大概是木有問題了- -b
真尼瑪歡樂的題目啊
附代碼,有興趣的童鞋幫我再叉一叉,有神數據請留言謝謝。。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
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<int> 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;
}
tarjan縮點後得到新圖
taskA是問新圖中無入度的點有多少
taskB是問max(新圖中無入度的點數,新圖中無出度的點數)
注意特判只有1個強連通分量的情況
tarjan縮點後,位於沒有出度的強連通分量內的點就是答案
強連通分量縮點
如果新圖有大於1個葉子,則無答案
否則答案爲位於葉子連通分量內的點的數量
問最少要幾條邊讓原圖變成強連通圖,老題目了,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找一條權和最大路徑即可
注意有負權點,直接無視負點即可
臥槽這什麼題面啊。。百度了才知道要幹嘛- -b
就是問點數大於等於2的強連通分量的數量
100次詢問u是不是能走到v
強連通分量縮點後每次從belong[u] 做一次bfs即可(突然覺得縮點過程可有可無。。就當優化好了- -b)
POJ3592 Instantaneous Transference
強連通分量縮點然後找最大權和路徑。
注意貌似有傳送到'#'的數據,我在這裏WA了兩次。
大概這是這套題裏唯一需要一點腦子的題了。。
題面給出了一幅二分圖和一組完美匹配,問每個左點可以和哪些右點連而不會破壞完美匹配
要做這題呢,大概你首先得會匈牙利求二分圖最大匹配
設王子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 <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
using namespace std;
#define snuke(it,x) for (vector<int>::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<int> 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<int> 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;
}