邻接表存图的小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;
}
原文地址:https://www.cnblogs.com/tztqwq/p/13651986.html