鄰接表存圖的小trick(存多個圖)


我常用如下的鄰接表存圖的方式:

int head[N], next[M*2], ver[M*2], edge_tot;
void add_edge(int x,int y) {
    ver[++edge_tot] = y;
    next[edge_tot] = head[x];
    head[x] = edge_tot;
}

對於要存多圖張圖的場景來說, 一個直觀的想法是將上面所用數組和函數大體復制一遍, 然后對命名獨立區分,即:

int head2[N], next2[M*2], ver2[M*2], edge_tot2;
void add_edge2(int x,int y) {
    ver2[++edge_tot2] = y;
    next2[edge_tot2] = head2[x];
    head2[x] = edge_tot2;
}

但是說白了, 鄰接表不過是從所有邊組成的集合里拿出一部分變成一條鏈掛在了標號上……

用鄰接表的方法存多張圖本質上就是對每個點存多個鄰接邊集, 每個鄰接邊集屬於某張圖。

所以存多個圖只需要多開 head 就行了(對於一張圖, 一條鏈就是一個點的鄰接邊集)。

常用的遍歷一條邊的算法就變成這樣:

for(int i=head[K][x];i;i=next[i]) {
    int y=ver[i], z=weight[i];
    \\ balabala
}

只需要在 i=head[x] 處做一些微小的改變, 以區分在不同圖中某個標號的點的相鄰邊, 這種區分也是必要的。

個人是推薦這種方法存多圖的, 因為大多數人還是挺熟悉鄰接表的, 學這種方法成本極低。

不過要得到一張圖的邊的數量嘛……再開數組記錄吧?

示例:

Luogu的DAG縮點+DP模板題

我扒出來以前的代碼

#include<bits/stdc++.h>
using namespace std;
const int N = 1e4+15;
const int M = 1e5+15;

int n, m, val[N], val2[N];

int ct, hd[N], nt[M<<1], vr[M<<1];
void ad(int a,int b) {
	vr[++ct]=b, nt[ct]=hd[a], hd[a]=ct;
}

int deg[N];
int ct2, hd2[N], nt2[M<<1], vr2[M<<1];
void ad2(int a,int b) {
	vr2[++ct2]=b, nt2[ct2]=hd2[a], hd2[a]=ct2;
}

int sccno[N], scccnt;
int S[N], tp;
int dfn[N], low[N], clk;
void dfs(int x) {
	dfn[x]=low[x]= ++clk;
	S[++tp]=x;
	for(int i=hd[x];i;i=nt[i]) {
		int y=vr[i];
		if(!dfn[y]) {
			dfs(y);
			low[x]=min(low[x],low[y]);
		} else if(!sccno[y]) low[x]=min(low[x],dfn[y]);
	}
	if(low[x]==dfn[x]) {
		++scccnt;
		while(1) {
			int u=S[tp--];
			sccno[u] = scccnt;
			if(u==x) break;
		}
	}
}

int f[N];
int q[N], h=1, t=0;
void topo() {
	for(int i=1;i<=scccnt;++i) if(!deg[i]) f[q[++t]=i] = val2[i];
	while(h<=t) {
		int x=q[h++];
		for(int i=hd2[x];i;i=nt2[i]) {
			int y=vr2[i];
			f[y] = max(f[y], f[x]+val2[y]);
			if(--deg[y] == 0) q[++t] = y;
		}
	}
}

int main()
{
	scanf("%d%d", &n, &m);
	for(int i=1;i<=n;++i) scanf("%d", &val[i]);
	for(int i=0,x,y;i<m;++i) {
		scanf("%d%d",&x,&y); ad(x,y);
	}
	for(int i=1;i<=n;++i) if(!dfn[i]) dfs(i);
	for(int x=1;x<=n;++x) {
		val2[sccno[x]] += val[x];
		for(int j=hd[x];j;j=nt[j]) {
			int y=vr[j];
			if(sccno[x] != sccno[y]) ad2(sccno[x],sccno[y]), ++deg[sccno[y]];
		}
	}
	
	topo();
	int ans = 0;
	for(int i=1;i<=scccnt;++i) ans=max(ans, f[i]);
	cout << ans;
	return 0;
}

修改一下, AC記錄

#include<bits/stdc++.h>
using namespace std;
const int N = 1e4+15;
const int M = 1e5+15;

int n, m, val[N], val2[N];

int ct, hd[3][N], nt[M<<2], vr[M<<2];
void ad(int id,int a,int b) {
	vr[++ct]=b, nt[ct]=hd[id][a], hd[id][a]=ct;
}

int deg[N];

int sccno[N], scccnt;
int S[N], tp;
int dfn[N], low[N], clk;
void dfs(int x) {
	dfn[x]=low[x]= ++clk;
	S[++tp]=x;
	for(int i=hd[1][x];i;i=nt[i]) {
		int y=vr[i];
		if(!dfn[y]) {
			dfs(y);
			low[x]=min(low[x],low[y]);
		} else if(!sccno[y]) low[x]=min(low[x],dfn[y]);
	}
	if(low[x]==dfn[x]) {
		++scccnt;
		while(1) {
			int u=S[tp--];
			sccno[u] = scccnt;
			if(u==x) break;
		}
	}
}

int f[N];
int q[N], h=1, t=0;
void topo() {
	for(int i=1;i<=scccnt;++i) if(!deg[i]) f[q[++t]=i] = val2[i];
	while(h<=t) {
		int x=q[h++];
		for(int i=hd[2][x];i;i=nt[i]) {
			int y=vr[i];
			f[y] = max(f[y], f[x]+val2[y]);
			if(--deg[y] == 0) q[++t] = y;
		}
	}
}

int main()
{
	scanf("%d%d", &n, &m);
	for(int i=1;i<=n;++i) scanf("%d", &val[i]);
	for(int i=0,x,y;i<m;++i) {
		scanf("%d%d",&x,&y); ad(1,x,y);
	}
	for(int i=1;i<=n;++i) if(!dfn[i]) dfs(i);
	for(int x=1;x<=n;++x) {
		val2[sccno[x]] += val[x];
		for(int j=hd[1][x];j;j=nt[j]) {
			int y=vr[j];
			if(sccno[x] != sccno[y]) ad(2,sccno[x],sccno[y]), ++deg[sccno[y]];
		}
	}
	
	topo();
	int ans = 0;
	for(int i=1;i<=scccnt;++i) ans=max(ans, f[i]);
	cout << ans;
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM