1. 程式人生 > >圖論 模板(2)

圖論 模板(2)

Tarjan演算法

割點(割頂)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define _ 200010
using namespace std;
struct node{int x,y,next;} a[_];
int n,m,len=0,id=0,ans=0;
int last[_],low[_],dfn[_];
/*dfn[i]表示點i被訪問的時間戳
low[i]表示點i及i的子樹中所有結點能到達的結點中dfn最小的結點的時間戳*/
bool bz[_];//是不是割點
void ins
(int x,int y) { a[++len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len; } void dfs(int x,int root) //x表示當前訪問到第x個點,root表示以root為根節點的子樹的根 { int tot=0;//入度(及子樹數) low[x]=dfn[x]=++id; //記錄時間戳 for(int i=last[x];i;i=a[i].next) { int y=a[i].y; if(!dfn[y]) { dfs(y,root); low[x]=min(low[x],low[y]);
//更新當前節點的low值 if(low[y]>=dfn[x]&&x!=root) bz[x]=true; /*非根且子樹能達到的dfn最小的結點的時間>=自己的時間時, 說明它的子樹中最早能訪問到的結點都比它後訪問,此時只要不為根就一定是割點*/ if(x==root) tot++; //更新入度 } low[x]=min(low[x],dfn[y]); //把點x及x的子樹可以達到的dfn的最小結點更新 } if(x==root&&tot>=2) bz[root]=true; /*如果一個點為根且入度>=2(即有兩個子樹),則一定為割點, 因為一棵樹的根一刪,那麼它的子樹一定不連通了*/
} int main() { int x,y; scanf("%d %d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d %d",&x,&y); ins(x,y); ins(y,x); } for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i,i); for(int i=1;i<=n;i++) if(bz[i]) ans++; printf("%d\n",ans);//割點的總數 for(int i=1;i<=n;i++) if(bz[i]) printf("%d ",i);//輸出割點 return 0; }

割邊(橋)

#include<bits/stdc++.h>
#define _ 110000
using namespace std;

struct rec
{
    int y, next;
}edge[_],cut[_];
int n,m,id=0,len=0;
int dfn[_], low[_];
int head[_], bz[_], link[_];

void Add(rec *edge,int x,int y,int *head)
{
    edge[++len].y = y,edge[len].next = head[x],head[x] = len;
}

void tarjan(int x)
{
    dfn[x]=low[x]=++id;
    for (int i =head[x]; i; i =edge[i].next) 
	{
        int y = edge[i].y;
        if (bz[x] == y) continue;
        if (!dfn[y]) 
		{
            bz[y] = x;//是y的爸爸為x!!
            tarjan(y);
            low[x] = min(low[x], low[y]);
            if (low[y] > dfn[x]) Add(cut, x, y, link);
        }//此處無=號!!
        low[x] = min(low[x], dfn[y]);
    }
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; ++i) 
	{
        int x, y;
        cin >> x >> y;
        Add(edge,x,y,head);
        Add(edge,y,x,head);
    }
    for (int i = 1; i <= n; ++i) 
		if (!dfn[i]) tarjan(i);
    for (int x = 1; x <= n; ++x) 
		for (int i = link[x]; i; i = cut[i].next)
            printf("%d %d\n",x,cut[i].y);
}

有向圖的強連通分量

//強連通分量
#include<bits/stdc++.h>
#define _ 100010
#define $ 1000010
using namespace std;
inline int read()
{
	int f=1,num=0;
	char ch=getchar();
	while (ch<'0'||ch>'9') { if (ch=='-') f=-1; ch=getchar(); }
	while (ch>='0'&&ch<='9') num=(num<<1)+(num<<3)+ch-'0', ch=getchar();
	return num*f;
}

int ver[$],Next[$],link[_],len=0;
void add(int x,int y)
{
	ver[++len]=y,Next[len]=link[x],link[x]=len;
}
int dfn[_],low[_],id=0,belong[_];
int Stack[_],top=0,tot=0;
int instack[_];
vector<int>scc[_];
void tarjan(int x)
{
	dfn[x]=low[x]=++id;
	instack[x]=1;
	Stack[++top]=x;
	for(int i=link[x];i;i=Next[i])
	{
		int y=ver[i];
		if(!dfn[y])
		{
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(instack[y])
		low[x]=min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x])
	{
		int k;
		++tot;
		do
		{
			k=Stack[top--], instack[k]=0;
			belong[k]=tot,	scc[tot].push_back(k);
		}while (k!=x);
	}
}
int main()
{
	int n=read(),m=read();
	for(int i=1;i<=m;++i)
	{
		int x=read(),y=read();
		add(x,y);
	}
	for(int i=1;i<=n;++i)
		if(!dfn[i])	tarjan(i);
	for (int i=1;i<=tot;++i)
	{
		printf("SCC #%d:",i);
		for (int j=0;j<scc[i].size();++j)
			printf(" %d",scc[i][j]);
		puts(" ");
	}
	return 0;
}

附上一個求最大強連通分量的例題

洛谷 p1726
描述 Description
在幻想鄉,上白澤慧音是以知識淵博聞名的老師。春雪異變導致人間之裡的很多道路都被大雪堵塞,使有的學生不能順利地到達慧音所在的村莊。因此慧音決定換一個能夠聚集最多人數的村莊作為新的教學地點。人間之裡由N個村莊(編號為1…N)和M條道路組成,道路分為兩種一種為單向通行的,一種為雙向通行的,分別用1和2來標記。如果存在由村莊A到達村莊B的通路,那麼我們認為可以從村莊A到達村莊B,記為(A,B)。當(A,B)和(B,A)同時滿足時,我們認為A,B是絕對連通的,記為<A,B>。絕對連通區域是指一個村莊的集合,在這個集合中任意兩個村莊X,Y都滿足<X,Y>。現在你的任務是,找出最大的絕對連通區域,並將這個絕對連通區域的村莊按編號依次輸出。若存在兩個最大的,輸出字典序最小的,比如當存在1,3,4和2,5,6這兩個最大連通區域時,輸出的是1,3,4。
輸入格式 Input Format
第1行:兩個正整數N,M
第2…M+1行:每行三個正整數a,b,t, t = 1表示存在從村莊a到b的單向道路,t = 2表示村莊a,b之間存在雙向通行的道路。保證每條道路只出現一次。
輸出格式 Output Format
第1行: 1個整數,表示最大的絕對連通區域包含的村莊個數。
第2行:若干個整數,依次輸出最大的絕對連通區域所包含的村莊編號。
樣例輸入 Sample Input

5 5
1 2 1
1 3 2
2 4 2
5 1 2
3 5 1

樣例輸出 Sample Output

3
1 3 5

時間限制 Time Limitation
1s
註釋 Hint
對於60%的資料:N <= 200且M <= 10,000
對於100%的資料:N <= 5,000且M <= 50,000

#include<bits/stdc++.h>
#define _ 100010
using namespace std;
inline int read()
{
	int f=1,num=0;
	char ch=getchar();
	while (ch<'0'||ch>'9') { if (ch=='-') f=-1; ch=getchar(); }
	while (ch>='0'&&ch<='9') num=(num<<1)+(num<<3)+ch-'0', ch=getchar();
	return num*f;
}
int head[_],ver[_],Next[_],len;
void add(int x,int y)
{
    ver[++len]=y,Next[len]=head[x],head[x]=len;
}
int dfn[_],low[_],id=0;
int belong[_],siz[_];
int Stack[_],top=0,tot=0;
int instack[_];
void tarjan(int x)
{	
    low[x]=dfn[x]=++id;
    instack[x]=1;
	Stack[++top]=x;
    for(int i=head[x];i;i=Next[i])
	{
		int y=ver[i];
        if(!dfn[y])
		{
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(instack[y])
			low[x]=min(low[x],dfn[y]);
    }
	int k;
    if(low[x]==dfn[x])
	{
    	++tot;
    	do
    	{
    		k=Stack[top];
			siz[tot]++;
    		--top;
    		instack[k]=0;
    		belong[k]=tot;
		} while (k!=x);
    }
}
int maxn=-1;
int main()
{
    int n=read(),m=read();
    for(int i=1;i<=m;i++)
	{
        int x=read(),y=read(),z=read();
        add(x,y);
        if(z==2) add(y,x);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i]) tarjan(i);
    int dcc;
    for(int i=1;i<=n;i++)
        if(siz[belong[i]]>maxn)
			maxn=siz[belong[i]],dcc=i;
    printf("%d\n",maxn);
    for(int i=1;i<=n;i++)
        if(belong[i]==belong[dcc]) printf("%d ",i);
    return 0;
}

無向圖的雙連通分量

E-DCC (邊-雙連通分量) 模板

/*
*邊雙連通分量(e-DCC)的求法
*/
#include<bits/stdc++.h>
#define _ 100010
using namespace std;
int head[_],ver[_*2],Next[_*2];
int dfn[_],low[_],n,m,tot,id;
bool bridge[_*2];
void add(int x,int y)
{
	ver[++tot]=y,Next[tot]=head[x],head[x]=tot;
}
void tarjan(int x,int inEdge)
{
	dfn[x]=low[x]=++id;
	for (int i=head[x];i;i=Next[i])
	{
		int y=ver[i];
		if (!dfn[y])
		{
			tarjan
            
           

相關推薦

模板2

Tarjan演算法 割點(割頂) #include<cstdio> #include<cstring> #include<algorithm> #define _ 200010 using namespace std; struct node{

模板1

最短路(ShortextPath) Dijkstra #include<bits/stdc++.h> using namespace std; /*Dijkstra演算法*/ int a[3010][3010],d[3010],n,m; bool v[3010];

模板3

一筆畫問題(尤拉路) DFS版 /*1.一筆畫問題 *規定 所有的邊都只能畫一次,不能重複畫 *輸入 第一行只有一個正整數N(N<=10)表示測試資料的組數. 每組測試資料的第一行有兩個正整數P,Q(P<=1000,Q<=2000), 分別表示這個畫中有多

講解1——基礎

同學 根據 tdi sin images 鄰接表 c++ algo ack 前面一直在嗶嗶數論,是不是感覺很煩的慌了?? ╮(╯▽╰)╭唉,你不煩得慌我都煩得慌了! 既然這樣,那我們就改個話題,今天我們就講講圖論。 有的同學就要問圖又是個什麽鬼? 難道是這個嗎?

第21課 可變參數模板2_展開參數包

delet pre 控制 seq src 構造 pro head del 1. 可變參數模板函數 (1)遞歸函數方式展開參數包   ①一般需要提供前向聲明、一個參數包的展開函數和一個遞歸終止函數。   ②前向聲明有時可省略,遞歸終止函數可以是0個或n個參數 (2)逗號表達式

【AI基礎】python:openCV——像處理2

getTrackbarPos圖像處理練習 制作一個滑動條調色板,使用函數cv2.getTrackbarPos();cv2.creatTrackbar() import cv2 import numpy as np def nothing(x): pass img = np.zeros((300,5

並不對勁的專題:SPFA算法的優化

a算法 bubuko 等於 dfs size iomanip 最小 bre else if 1.bzoj1489-> 這是個新套路。 我們希望找到最小的x,那麽可以二分x,然後判斷是否存在圈的邊權的平均值小於等於x。 設圈的邊權依次為w1,w2,w3,…,wk,平均值

python像處理2像水印和PIL模式轉化

tex 我們 參考 height rom 設置 分享 show pen 模式轉化: PIL模式轉化:將圖片轉化成其他模式 1 # 我們將image圖像轉化為灰度圖像(python) 2 from PIL import Image 3 img = Image.open

Django基於第一個項目導入模板2

分享 template bubuko 項目 服務器 http brush 新建文件夾 temp 1.在模塊blog下新建文件夾templates,在templates下新建index.html文件 //index.html代碼如下: <html> <

PAT 備考第一天——演算法

大綱: 必考考點: 1.圖的定義和相關術語 2.圖的儲存(鄰接矩陣和鄰接表) 3.圖的遍歷(DFS和BFD) 4.最短路徑演算法 5.拓撲排序 非重點考點: 1.關鍵路徑 2.最短路徑中的Bellman-Ford和SPFA 甲級考綱以外的考點: 最小生成樹演算法 一、圖的

尤拉哈密頓

圖論:尤拉圖與哈密頓圖 圖論最基本的要素就是點和邊,尤拉圖和哈密頓圖是分別關於點和邊的兩種特殊圖的形式。 尤拉圖側重於經過所有的點,哈密頓圖側重於經過所有的邊。 尤拉圖 尤拉路徑:一條路徑在圖G中恰好經過每條邊一次。尤拉通路:通過圖中所有邊的簡單路(其實就是每條邊經過

演算法-- 二分的最大分配問題JAVA

二分圖:又稱二部圖,如果一個圖的所有頂點可以被分為X和Y兩個集合,並且所有邊的兩個頂點恰好一個屬於一個集合X,另一個屬於集合Y,即每個集合內的頂點沒有邊相連,那麼這個圖就是二分圖。 二分圖的最大分配問

演算法

圖的基本概念及表示 圖的基本概念 圖(graph)可視為一有序二元組G = (V,E), 其中V = {v1,v2,……,vn}為頂點集,V中的元素稱為頂點(vertex),E = {e1,e2,……,en}稱為邊集,E中的元素成為邊(edge)。E

演算法3--- A*演算法求k短路

在一個有向圖中,如何求出前k短路,比較高效的演算法我們自然想到了A*演算法,這裡賦上程式碼,所有講解將放在程式碼註釋中: 如對A*有基礎性知識的疑問,請參考此文:A*搜尋演算法 import java.io.File; import java.io.IOException;

演算法4 --- TSP旅行商問題 求最短迴路acm

對於TSP旅行商問題,我們做的最多的也就是求最短迴路了,那麼對於一個數據量適中的圖來說,一般的dfs方法即可求解,在這裡,我應用dfs的思想來實現此問題,而關鍵之處在於對矩陣的改進,這樣的操作可以使得應用搜索思想求TSP問題時,效率有顯著的提高。對於矩陣的改進,我們對矩陣的

演算法--最短路徑的DFS/BFS解法JAVA

最短路徑–城市路徑問題: 問題描述:求從1號城市到5號城市的最短路徑長度 Input: 5 8 1 2 2 1 5 10 2 3 3 2 5 7 3 1 4 3 4 4

python科學計算學習二:matplotlib繪圖,標註釋2

    圖標註釋對於搞研究的人來說是很重要的,一般的paper裡面也都會有。所以是很有必要要學習的。     下面通過一個例子來說明python是怎麼做的。 1 import matplotlib.pyplot as plt 2 import numpy as n

演算法--求解割點、割邊JAVA

割點:對於一個連通圖來說,如果刪除某個點之後圖不再連通,這個點就稱為割點 割點演算法 時間複雜度:O(N+M) 但是下面給出的演算法時間複雜度為O(N^2),這是因為下面的儲存結構都是鄰接矩陣,這

PAT 備考——演算法最短路徑

最短路徑演算法是PAT甲級考試常考演算法,具體說來,最短路徑包括Dijkstra演算法、Floyd演算法,其餘的Bellman-Ford和SPFA基本不會考(參《演算法筆記》胡凡,曾磊著) 目錄 一、最短路徑基本概念與問題分類 1.基本概念 2.問題分類 二、D

NOIP複賽複習程式對拍與模板

程式對拍 所謂“對拍”,顧名思義,就是讓兩者相互比對。所謂“兩者”,一是你要測試的程式,二是一個答案在該程式在一定範圍(時間/空間)內結果必定正確的程式(一般是用暴力求解的程式)。對拍一般需要造資料程式(data.exe),保證正確性的暴力對拍程式(test.exe)與測試程式(以moo.e