平面图与对偶图

平面图:如果能把图\(G\)画在平面上,使得除顶点外,边与边没有交叉,则称\(G\)可以嵌入平面,或称\(G\)为可平面图。可平面图\(G\)的一种边不交叉的画法,称为\(G\)的一种平面嵌入,\(G\)的平面嵌入表示的图称为平面图。
对偶图:对偶图是伴随平面图的一张图,具体来说就是把平面图里的每个面当成一个节点,这些节点之间的边是和原图的边相交的,对偶图边的权值等于原平面图边的权值。
形式化来说:设平面图为\(G\),其对偶图为\(G^{'}\),对于原图中每条边\(e\)
\(1\):若\(e\)在平面图中属于两个平面\(f_1,f_2\),则在对偶图中连边\((f_1^{'},f_2^{'})\)
\(2\):若\(e\)在平面图中属于一个平面\(f_1\),则在对偶图中连自环\((f_1^{'},f_1^{'})\)
有如下定理:平面图的最小割等于其对偶图的最短路。
所以当遇到平面图最小割问题时,若点数过多,导致网络流算法不易通过时,可考虑刻画出此平面图的对偶图,并在对偶图上跑最短路即可。
通过如下几道例题来深入理解:
\(P2046\)
https://www.luogu.com.cn/problem/P2046
题解:引理\(1\):将所有海拔赋为\(0\)\(1\)是最优的。
证明:每次选择海拔绝对值最大的点集,若绝对值大于\(1\),则将点集中所有点海拔根据正负加减\(1\),这样一定不会变劣。如此进行下去,直到图中只有海拔为\(0\)\(1\)的点。
引理\(2\):最终图一定由一个海拔为\(0\)的连通块和一个海拔为\(1\)的连通块构成。
证明:如果连通块个数\(>2\),则一定有一个连通块被完全包围,将这个被完全包围的连通块改变海拔,这样一定不会变劣。如此进行下去,直到图中只有两个连通块。
考虑这两个连通块代表的权值,正是这个平面图的一个割,但由于此图的点数过多,常规的网络流算法并不能通过。
考虑将平面图转化为对偶图。具体来说,先将起点和终点连上一条虚拟边,这样新增了一个面。令新增的这个面为\(S\),剩余那个无穷大的面为\(T\),按照平面图转对偶图的方式连边,发现新图中\(S\)\(T\)的任何一条路径,都完成了对原图的一个分割,同时将原图中的\(s\)\(t\)分割到两个集合中,并且路径长度,即为原图割的大小,于是直接在新建的对偶图中求\(S\)\(T\)的最短路即可。

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> PII;
const int N=510,M=2e6+10;

int n,S,T;
int d[M];
int h[M],e[M],w[M],ne[M],idx;
int dl[N][N],dr[N][N],du[N][N],dd[N][N];
bool st[M];

int get(int x,int y){
	return (x-1)*n+y;
}

void add(int a,int b,int c){
	e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

int dijkstra(){
	memset(d,0x3f,sizeof d);
	priority_queue<PII,vector<PII>,greater<PII> > heap;
	d[S]=0,heap.push({d[S],S});
	while(heap.size()){
		int t=heap.top().second;
		heap.pop();
		if(st[t]) continue;
		st[t]=1;
		for(int i=h[t]; i!=-1; i=ne[i]){
			int j=e[i];
			if(st[j]) continue;
			if(d[t]+w[i]<d[j]){
				d[j]=d[t]+w[i];
				heap.push({d[j],j});
			}
		}
	}
	return d[T];
}

int main(){
	memset(h,-1,sizeof h);
	cin >> n;
	for(int i=1; i<=n+1; i++)
		for(int j=1; j<=n; j++) cin >> dr[i][j];
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n+1; j++) cin >> dd[i][j];
	for(int i=1; i<=n+1; i++)
		for(int j=1; j<=n; j++) cin >> dl[i][j];
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n+1; j++) cin >> du[i][j];
	S=n*n+1,T=n*n+2;
	for(int i=1; i<=n; i++) add(S,get(i,1),dd[i][1]);
	for(int i=1; i<=n; i++) add(get(i,n),T,dd[i][n+1]);
	for(int i=1; i<=n; i++) add(S,get(n,i),dr[n+1][i]);
	for(int i=1; i<=n; i++) add(get(1,i),T,dr[1][i]);
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n-1; j++) add(get(i,j),get(i,j+1),dd[i][j+1]);
	for(int i=1; i<=n; i++)
		for(int j=2; j<=n; j++) add(get(i,j),get(i,j-1),du[i][j]);
	for(int i=1; i<=n-1; i++)
		for(int j=1; j<=n; j++) add(get(i,j),get(i+1,j),dl[i+1][j]);
	for(int i=2; i<=n; i++)
		for(int j=1; j<=n; j++) add(get(i,j),get(i-1,j),dr[i][j]);
	cout << dijkstra() << endl;
	return 0;
}

\(P4001\)
https://www.luogu.com.cn/problem/P4001
题解:同样发现所求的即为最小分割,仿照上一题做法即可。

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> PII;
const int N=5e6+10;

int n,m,S,T;
int d[N];
int h[N],e[N],w[N],ne[N],idx;
bool st[N];

int get(int x,int y){
	if(x>n-1||y<1) return S;
	if(x<1||y>2*(m-1)) return T;
	return (x-1)*2*(m-1)+y;
}

void add(int a,int b,int c){
	e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

int dijkstra(){
	memset(d,0x3f,sizeof d);
	priority_queue<PII,vector<PII>,greater<PII> > heap;
	d[S]=0,heap.push({d[S],S});
	while(heap.size()){
		int t=heap.top().second;
		heap.pop();
		if(st[t]) continue;
		st[t]=1;
		for(int i=h[t]; i!=-1; i=ne[i]){
			int j=e[i];
			if(st[j]) continue;
			if(d[t]+w[i]<d[j]){
				d[j]=d[t]+w[i];
				heap.push({d[j],j});
			}
		}
	}
	return d[T];
}

int main(){
	memset(h,-1,sizeof h);
	cin >> n >> m;
	S=(n-1)*2*(m-1)+1,T=(n-1)*2*(m-1)+2;
	for(int i=1; i<=n; i++)
		for(int j=1; j<=m-1; j++){
			int v;
			cin >> v;
			if(i>=2&&i<=n-1) add(get(i-1,2*j-1),get(i,2*j),v);
			add(get(i,2*j),get(i-1,2*j-1),v);
		}
	for(int i=1; i<=n-1; i++)
		for(int j=1; j<=m; j++){
			int v;
			cin >> v;
			add(get(i,2*(j-1)),get(i,2*(j-1)+1),v);
			if(j>=2&&j<=m-1) add(get(i,2*(j-1)+1),get(i,2*(j-1)),v);
		}
	for(int i=1; i<=n-1; i++)
		for(int j=1; j<=m-1; j++){
			int v;
			cin >> v;
			add(get(i,2*j-1),get(i,2*j),v);
			add(get(i,2*j),get(i,2*j-1),v);
		}
	cout << dijkstra() << endl;
	return 0;
}
posted @ 2025-01-17 17:36  lastxuans  阅读(97)  评论(0)    收藏  举报