Tarjan演算法_縮點
我們這一篇是在已經瞭解Tarjan演算法的基礎之上開始寫的,如果不瞭解的話,請先看大牛們關於Tarjan演算法的部落格。
首先我們先看一下一個問題:一個有向圖,有n個點以及m條邊,我們至少應該新增幾條邊才能使整個圖變成強連通圖。或者是一個無向圖至少新增幾條邊變成連通圖。
首先我們對於一個有向無環的圖(DAG),至少新增幾條邊才能使它變為強連通圖?我們很容易根據有向無環圖的性質得到,我們計算入度為零的點數為a,出度為零的點數為b,那麼我們至少需要新增的邊數為max(a,b),如果只有一個點的話,我們不需要新增任何邊。
那麼我們怎麼把一個圖轉換為DAG呢,因為上面給出的圖可能存在環,那麼我們就會想到把已經組成全連通的子圖轉換成一個點來看,那麼我們最終的圖就不會包含環了。
好了,解決這類問題的思路已經想好了,下面我們來進行求解:
我們使用Tarjan演算法求解出強連通分量之後,我們使用一個belong陣列將同一個連通分量的點分配相同的數值,存放在belong陣列中,然後我們再次遍歷一遍點,然後這次操作的是belong陣列中對應的數值,只有把不屬於同於個連通分量的邊新增到新的圖中,並且根據這些邊來計算每個縮點的入度以及出度。
//不怕別人比你聰明,就怕別人比你聰明還比你努力
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include <set>
#include <map>
#include<vector>
#define INF 0x3f3f3f3f
using namespace std;
const int MAXN = 1000;
struct Node
{
int fr;
int v;
int next1;
}edge1[MAXN],edge2[MAXN]; //edge1表示還沒有縮點之前的圖,edge2表示縮點之後的圖的連通關係
int head[MAXN];
int dfn[MAXN],low[MAXN];
int vis[MAXN],stact[MAXN];
int belong[MAXN],num[MAXN];
//belong表示每個點屬於的縮完之後的哪一個點,num表示每一個縮點裡面有多少個點
int cnt,tot,index,now;
void add(int x,int y,Node* edge)
{
edge[++cnt].next1 = head[x];
edge[cnt].v = y;
edge[cnt].fr = x;
head[x] = cnt;
}
void Tarjan(int x)
{
low[x] = dfn[x] = ++tot;
vis[x] = 1;
stact[++index] = x;
for(int i = head[x];i != -1; i = edge1[i].next1)
{
int v = edge1[i].v;
if(!dfn[v])
{
Tarjan(v);
low[x] = min(low[x], low[v]);
}
else if(vis[v])
low[x] = min(low[x], dfn[v]);
}
if(low[x] == dfn[x])
{
++now;
do
{
printf("%d ",stact[index]);
vis[stact[index]] = 0;
belong[stact[index]] = now;
num[now] ++;
index --;
}while(x != stact[index+1]);
printf("\n");
}
return ;
}
int main()
{
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(vis,0,sizeof(vis));
memset(stact,0,sizeof(stact));
memset(belong,0,sizeof(belong));
memset(num,0,sizeof(num));
cnt = 0;tot = 0;index = 0;now = 0;
//now最終表示縮點之後點的數目
int n,m;
scanf("%d%d",&n,&m);
int x,y;
for(int i = 1;i <= m ;i ++)
{
scanf("%d%d",&x,&y);
add(x,y,edge1);
}
for(int i = 1;i <= n;i ++)
if(!dfn[i])
Tarjan(i);
int inde[MAXN];//表示每個縮點的入讀
int outde[MAXN];//每個縮點的出度
//縮點完成之後,我們就一定沒有環的存在
memset(head,-1,sizeof(head));
int u,v;
for(int i = 1;i <= m;i ++)
{
u = belong[edge1[i].fr];
v = belong[edge1[i].v];
//表示如果這條邊不在縮點之內,那麼就是用來連線縮點
if(u!=v)
{
add(u,v,edge2);
inde[v] ++;
outde[u] ++;
}
}
int a = 0,b = 0;
//分別計算所的縮點中,入度和出度為0的數目
for(int i = 1;i <= now;i ++)
{
if(!inde[i]) a++;
if(!outde[i]) b++;
}
if(now == 1)
//如果所有的縮點只有一個,則不需要新增新邊
printf("0\n");
else
printf("%d\n",max(a,b));
}
6 8
1 3
1 2
2 4
3 4
3 5
4 6
4 1
5 6
我們的測試資料如上,答案是需要新增一條邊。
我們來看一個例題:Poj 2186
我們要想知道有多說少被全部的認為是受歡迎的,我麼先要將他們進行完縮點之後,只有其他的點都可以到達的點才是被其它都歡迎的點。
//不怕別人比你聰明,就怕別人比你聰明還比你努力
//因為和上面程式很類似,所以沒有寫註釋....
include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include <set>
#include <stack>
#include <map>
#include<vector>
#define INF 0x3f3f3f3f
using namespace std;
const int MAXN = 51000;
struct Node
{
int fr;
int v;
int next1;
}edge1[MAXN];
int head[MAXN];
int dfn[MAXN],low[MAXN];
int vis[MAXN],stact[MAXN];
int belong[MAXN],num[MAXN];
int cnt,tot,index,now;
int n,m;
void add(int x,int y,Node* edge)
{
edge[++cnt].next1 = head[x];
edge[cnt].v = y;
edge[cnt].fr = x;
head[x] = cnt;
}
void Tarjan(int x)
{
low[x] = dfn[x] = ++tot;
vis[x] = 1;
stact[++index] = x;
for(int i = head[x];i != -1; i = edge1[i].next1)
{
int v = edge1[i].v;
if(!dfn[v])
{
Tarjan(v);
low[x] = min(low[x], low[v]);
}
else if(vis[v])
low[x] = min(low[x], dfn[v]);
}
if(low[x] == dfn[x])
{
++now;
do
{
vis[stact[index]] = 0;
belong[stact[index]] = now;
num[now] ++;
index --;
}while(x != stact[index+1]);
}
return ;
}
void Init()
{
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(vis,0,sizeof(vis));
memset(stact,0,sizeof(stact));
memset(belong,0,sizeof(belong));
memset(num,0,sizeof(num));
cnt = 0;tot = 0;index = 0;now = 0;
scanf("%d%d",&n,&m);
int x,y;
for(int i = 1;i <= m ;i ++)
{
scanf("%d%d",&x,&y);
add(x,y,edge1);
}
}
int main()
{
Init();
for(int i = 1;i <= n;i ++)
if(!dfn[i])
Tarjan(i);
int u,v;
int outde[MAXN] = {0};
for(int i = 1;i <= m;i++)
{
u = belong[edge1[i].fr];
v = belong[edge1[i].v];
if(u != v)
{
outde[u]++;
}
}
int ans = 0;
for(int i =1;i <= now;i ++)
{
if(!outde[i])
{
if(ans > 0)
{
printf("0\n");
return 0;
}
ans = num[i];
}
}
printf("%d\n",ans);
return 0;
}
相關文章
- tarjan演算法求scc & 縮點演算法
- 【模板】tarjan 強連通分量縮點
- Tarjan縮點題單 刷題題解
- 【演算法學習】tarjan 強連通、點雙、邊雙及其縮點 重磅來襲!!!!演算法
- tarjan縮點-受歡迎的牛-筆記筆記
- 強連通------tarjan演算法詳解及與縮點聯合運用演算法
- 【Tarjan 拓撲排序 dp】P3387 【模板】縮點排序
- POJ2533&&SP1799 The Bottom of a Graph(tarjan+縮點)
- tarjan—演算法的神(一)演算法
- Tarjan 演算法學習筆記演算法筆記
- 連通圖與Tarjan演算法演算法
- 強連通分量(Tarjan演算法)演算法
- Tarjan演算法(強連通分量分解)演算法
- UVA1327 && POJ1904 King's Quest(tarjan+巧妙建圖+強連通分量+縮點)
- 圖論——強連通分量(Tarjan演算法)圖論演算法
- Tarjan
- 一文輕鬆搞定 tarjan 演算法(二)演算法
- 連通圖演算法詳解之① :Tarjan 和 Kosaraju 演算法演算法
- 縮點
- kosaraju 和 tarjan演算法詳解(強連通分量)演算法
- POJ 1330 LCA最近公共祖先 離線tarjan演算法演算法
- tarjan[模板]
- SCC縮點
- 強連通分量及縮點 演算法解析及例題演算法
- tarjan2
- 尋找圖的強連通分量:tarjan演算法簡單理解演算法
- Tarjan再學習
- LCA(倍增與Tarjan)
- 壓縮演算法一覽演算法
- 壓縮字串《演算法很美》字串演算法
- Tarjan 學習筆記筆記
- tarjan學習筆記筆記
- HDU 2586 倍增 / Tarjan LCA
- 強聯通分量tarjan
- 20行程式碼實現,使用Tarjan演算法求解強連通分量行程演算法
- 可伸縮聚類演算法綜述(可伸縮聚類演算法開篇)聚類演算法
- 圖片縮小尺寸演算法演算法
- HDU 4738 Caocao's Bridges(Tarjan)