平面图最小割转对偶图最短路。
第一眼看到这题,显然是最小割嘛。。。根据最大流最小割定理,跑一遍最大流即可,但复杂度 O(n2∗m) ,显然要T啊。
然后我就学习了平面图最小割转对偶图最短路的想法,看完就会做了,资料传送门:两极相通——浅析最大最小定理在信息学竞赛中的应用
注意,对偶图最短路做法只使用于s-t平面图,不一定适用于一般图
这里顺便摘抄一些平面图的重要定义和性质,详细内容看资料:
·平面图:若图G可画在平面上,使得任意两条边都不会在非端点处相交,则称图G是平面图。
·s-t平面图:平面图中的一个点为源点s,另外一个点为汇点t,且s和t都在图中的无界面的边界上,这样的平面图称为s-t平面图
·欧拉公式:如果一个连通的平面图有n个点,m条边,f个域(面),那么f=m-n+2
·性质:每个平面图G都有一个与其对偶的平面图G*
话说这题用SPFA慢出翔了,推荐堆优化dijkstra(复杂度 O((n+m)∗log2n) )
#include<cstdio> #include<queue> #include<cstring> #define add2(u,v,w) add(u,v,w);add(v,u,w) #define N 1005 using namespace std; int in() { int r=0; char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')r=r*10+ch-'0',ch=getchar(); return r; } queue<int> q; bool vis[2200000]; int last[2200000], d[2200000], s=0, t=1, cnt=0; int pack(int x, int y, int side){return (x*1001+y)+side*1002005;} struct edge{int next,to,v;}e[2*3*N*N]; void add(int a, int b, int w) { e[++cnt]=(edge){last[a],b,w}; last[a]=cnt; } struct node { int id, v; node(){} node(int a, int b):id(a),v(b){} bool operator < (const node &a) const { return a.v<v; } }; int dijkstra() { priority_queue<node> q; memset(d,63,sizeof(d)); q.push(node(s,d[s]=0)); vis[s]=1; while(!q.empty()) { int x=q.top().id; q.pop(); vis[x]=1; if(x==t)break; for(int i = last[x]; i; i=e[i].next) { int y=e[i].to; if(!vis[y]&&d[x]+e[i].v<d[y]) { d[y]=d[x]+e[i].v; q.push(node(y,d[y])); } } } return d[t]; } int main() { int n, m, len; n=in();m=in(); if(n==1&&m==1)return !printf("0\n"); for(int i = 1; i <= n; i++) for(int j = 1; j < m; j++) { len=in(); add2(pack(i,j,1),pack(i-1,j,0),len); } for(int i = 1; i < n; i++) for(int j = 1; j <= m; j++) { len=in(); add2(pack(i,j,0),pack(i,j-1,1),len); } for(int i = 1; i < n; i++) for(int j = 1; j < m; j++) { len=in(); add2(pack(i,j,0),pack(i,j,1),len); } for(int j = 1; j <= m; j++) { add(pack(0,j,0),t,0); add(s,pack(n,j,1),0); } for(int i = 1; i <= n; i++) { add(pack(i,m,0),t,0); add(s,pack(i,0,1),0); } int ans=dijkstra(); printf("%d\n",ans); }