用tarjan 的強連通分量增加了兩句話得到的求邊連通分量的方法
也就是因爲無向圖加了兩條邊之後有連回去的邊,所以要判斷下一個子節點是不是父親節點,其餘的和有向圖的強連通分量沒差。
這個算法就是記錄兩個時間戳,一個是index,一個是low。不斷dfs。然後通過子節點更新low 這樣把子節點更新完之後,屬於同一個邊連通分量(可以兩條路徑經過同一個點,但是不能經過通一條邊的叫做邊連通分量)的點都入棧了。然後當前時間戳即爲最小時間戳,此時退棧並記錄節點。
<pre name="code" class="cpp">stack<int>sta;
void init2()
{
e = 0;
memset(head,-1,sizeof(head));
}
struct p1
{
int index,low;
}nar[SIZE_D];
int Dindex,scc;
int used[SIZE_D],insta[SIZE_D],belong[SIZE_D],num[SIZE_D],par[SIZE_D];
void inittarjan()
{
Dindex = 0;
memset(insta,0,sizeof(insta));
scc = 0;
memset(num,0,sizeof(num));
}
void tarjan(int ver)
{
used[ver] = 1;
nar[ver].index = Dindex;//記錄當前的時間戳
nar[ver].low = Dindex;//記錄最早的時間戳
Dindex++;//時間戳++;
sta.push(ver);//當前節點入棧
insta[ver] = 1;
for (int i = head[ver]; i != -1; i = pra[i].next){
int u = pra[i].to;
if (used[u] == -1){//遍歷子節點。如果子節點沒有被訪問過,則dfs
par[u] = ver;
tarjan(u);
nar[ver].low = min(nar[ver].low, nar[u].low);//最早的時間戳更新
}else{
if (insta[u] == 1)//如果已經在棧內。更新當前的最早時間戳
if (u != par[ver])
nar[ver].low = min(nar[ver].low, nar[u].low);
}
}
if (nar[ver].low == nar[ver].index){//dfs結束後回到了最早的時間戳的那個頂點
scc++;
int w;
do{
w = sta.top();//出棧
sta.pop();
insta[w] = 0;
belong[w] = scc;//記錄這個節點屬於哪個連通分量
num[scc]++;//這個連通分量裏面的節點個數加一
}while (w != ver);//直到取到當前節點爲止。
}
}
題意:讓你最少加多少條邊可以沒有橋(就是去掉這條邊,就不連通了。這條邊就是橋),可能會有重邊
題解:縮點——用tarjan 的邊連通分量可以縮點。
然後結果就是(出度爲1的點的個數+1)/ 2
判斷每個節點是否和子節點屬於同一個連通分量,如果不屬於。那麼這兩個連通分量自加1,注意是強連通分量+1不是頂點+1
因爲是無向圖 所以出度爲2就是有向圖裏的出度爲1
這個重邊好坑啊!
思考重邊會不會對結果造成影響,然後讀入的時候要判斷一下有沒有重邊。引申,讀入還要判斷一下有沒有自環!!!(自己連到自己的節點上)
思考重邊會不會對結果造成影響,然後讀入的時候要判斷一下有沒有重邊。引申,讀入還要判斷一下有沒有自環!!!
思考重邊會不會對結果造成影響,然後讀入的時候要判斷一下有沒有重邊。引申,讀入還要判斷一下有沒有自環!!!
思考重邊會不會對結果造成影響,然後讀入的時候要判斷一下有沒有重邊。引申,讀入還要判斷一下有沒有自環!!!
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<stack>
#define SIZE_D 5005
#define SIZE_B 50005
using namespace std;
int e,head[SIZE_D];
struct pp
{
int to,next;
}pra[SIZE_B];
void addedge(int x,int y)
{
pra[e].to = y;
pra[e].next = head[x];
head[x] = e++;
pra[e].to = x;
pra[e].next = head[y];
head[y] = e++;
}
int repeat(int x, int y)
{
for (int i = head[x]; i != -1; i = pra[i].next){
if (pra[i].to == y)
return 0;
}
return 1;
}
int out[SIZE_D];
int D,B;
stack<int>sta;
void init2()
{
e = 0;
memset(head,-1,sizeof(head));
}
struct p1
{
int index,low;
}nar[SIZE_D];
int Dindex,scc;
int used[SIZE_D],insta[SIZE_D],belong[SIZE_D],num[SIZE_D],par[SIZE_D];
void inittarjan()
{
Dindex = 0;
memset(insta,0,sizeof(insta));
scc = 0;
memset(num,0,sizeof(num));
}
void tarjan(int ver)
{
used[ver] = 1;
nar[ver].index = Dindex;//記錄當前的時間戳
nar[ver].low = Dindex;//記錄最早的時間戳
Dindex++;//時間戳++;
sta.push(ver);//當前節點入棧
insta[ver] = 1;
for (int i = head[ver]; i != -1; i = pra[i].next){
int u = pra[i].to;
if (used[u] == -1){//遍歷子節點。如果子節點沒有被訪問過,則dfs
par[u] = ver;
tarjan(u);
nar[ver].low = min(nar[ver].low, nar[u].low);//最早的時間戳更新
}else{
if (insta[u] == 1)//如果已經在棧內。更新當前的最早時間戳
if (u != par[ver])
nar[ver].low = min(nar[ver].low, nar[u].low);
}
}
if (nar[ver].low == nar[ver].index){//dfs結束後回到了最早的時間戳的那個頂點
scc++;
int w;
do{
w = sta.top();//出棧
sta.pop();
insta[w] = 0;
belong[w] = scc;//記錄這個節點屬於哪個連通分量
num[scc]++;//這個連通分量裏面的節點個數加一
}while (w != ver);//直到取到當前節點爲止。
}
}
int main()
{
//freopen("input.txt","r",stdin);
while (~scanf("%d %d",&D,&B)){
inittarjan();
init2();
for (int i = 1; i <= B; i++){
int tempx,tempy;
scanf("%d %d",&tempx,&tempy);
if (repeat(tempx,tempy))
addedge(tempx,tempy);
}
memset(used,-1,sizeof(used));
for (int i = 1; i <= D; i++)
if (used[i] == -1){
par[i] = -1;
tarjan(i);
}
//tarjan();
int res = 0;
memset(out,0,sizeof(out));
for (int i = 1; i <= D; i++){//在別的題目裏面有可能不是1到D爲序號!!
for (int j = head[i]; j != -1;j = pra[j].next){
int u = pra[j].to;
if (belong[pra[j].to] != belong[i]){//判斷每個節點是否和子節點屬於同一個連通分量,如果不屬於。那麼這兩個連通分量自加1,注意是強連通分量+1不是頂點+1
out[belong[pra[j].to]]++;
out[belong[i]]++;
}
//printf("delong[%d] = %d belong[%d] = %d\n",i,belong[i],u,belong[u]);
}
}
//printf("%d\n",scc);
for (int i =1; i <= scc; i++)
if (out[i] == 2){//注意,因爲是無向圖 所以出度爲2就是有向圖裏的出度爲1
//printf("i=%d\n",i);
res++;
}
printf("%d\n",(res+1)/2);
}
return 0;
}