bzoj2007/luoguP2046 海拔(平面图最小割转对偶图最短路)
题目描述:
题解时间:
首先考虑海拔待定点的$h$都应该是多少
很明显它们都是$0$或$1$,并且所有$0$连成一块,所有$1$连成一块
只有海拔交界线对答案有贡献,变成了最小割
但是数据范围很明显不能直接跑网络流
由于这是一个平面图,所以根据套路想到:
平面图最小割=对偶图最小环=最外一块面积分成$S$和$T$跑最短路
从左上角往右下角画一条线把外面一块分成$S$和$T$之后建图。
但是要注意这张图上同一条边两个方向权值不同。
那么建边也按照相同方向,即对应向右下方向的边的新建边为$S$->$T$方向,向左上的反之。
然后就可以跑最短路了。
#include<cstdio> #include<algorithm> #include<cstring> #include<queue> using namespace std; typedef long long lint; namespace LarjaIX { const int N=511; int n,id[N][N]; struct sumireko{int to,ne,w;}e[N*N*8]; int he[N*N*2],ecnt; void addline(int f,int t,int w) { e[++ecnt].to=t,e[ecnt].w=w; e[ecnt].ne=he[f],he[f]=ecnt; } struct shion { int x;lint d; shion(){} shion(int x,lint d):x(x),d(d){} bool operator < (const shion &a)const{return d>a.d;} }stmp; priority_queue<shion>q; lint dis[N*N*2]; bool vis[N*N*2]; void dijkstra(int sp,int ep) { memset(dis,0x3f,sizeof(dis)); q.push(shion(sp,dis[sp]=0)); while(!q.empty()) { stmp=q.top(),q.pop(); int x=stmp.x; if(vis[x]) continue;vis[x]=1; for(int i=he[x],t=e[i].to;i;i=e[i].ne,t=e[i].to) { if(dis[t]>dis[x]+e[i].w) { dis[t]=dis[x]+e[i].w; q.push(shion(t,dis[t])); } } } printf("%lld ",dis[ep]); } int wi; int maid() { #ifdef debug freopen("sample.in","r",stdin); freopen("debug.out","w",stdout); #endif scanf("%d",&n); for(int i=1;i<=n;i++)id[i][0]=id[n+1][i]=0,id[0][i]=id[i][n+1]=n*n+1; for(int i=1;i<=n;i++)for(int j=1;j<=n;j++) id[i][j]=(i-1)*n+j; for(int i=0;i<=n;i++)for(int j=1;j<=n;j++) scanf("%d",&wi),addline(id[i+1][j],id[i][j],wi); for(int i=1;i<=n;i++)for(int j=0;j<=n;j++) scanf("%d",&wi),addline(id[i][j],id[i][j+1],wi); for(int i=0;i<=n;i++)for(int j=1;j<=n;j++) scanf("%d",&wi),addline(id[i][j],id[i+1][j],wi); for(int i=1;i<=n;i++)for(int j=0;j<=n;j++) scanf("%d",&wi),addline(id[i][j+1],id[i][j],wi); dijkstra(0,n*n+1); return 0; } } int main(){return LarjaIX::maid();}