Luogu2046 NOI2010 海拔 - 网络流 - 最短路 -

题目链接:https://www.luogu.com.cn/problem/P2046

首先观察可以发现最优解一定是左上部分是全0,右下是全1这样的形式
然后题目就相当于让我们求一个 \((1,1) \rightarrow (n+1,n+1)\) 的最小割
由于这是网格图(属于平面图)。平面图最小割 = 对偶图最短路
于是转化成对偶图求最短路,那么如何建图呢?
我们不妨把前n行前n列抽出来,然后用一个矩形左上角的那个点的坐标记为这个矩形的坐标。这样我们就表示出了 \(n \times n\) 个小矩形
然后边有4种,这里取 \(A \rightarrow B\)\(C \rightarrow D\) 为例

\(A \rightarrow B\):这里是从东往西走,注意这个割对应的最短路一定是从右上往左下走的,所以 A->B 对应的鸽就是从下往上的了,也就是说,如果 \(B(i,j) ,A(i,j+1)\) 那么建图的时候就是下格子(坐标为B点坐标)\((i,j)\) 连向上格子(坐标为A点坐标都减去1) \((i-1,j+1-1) = (i-1,j)\)

\(C \rightarrow D\)同理。这里是从南往北走,对应的割的路线是左往右,也就是说 \(D(i,j)\) \(C(i+1,j)\),则连边为 \((i+1-1,j-1)=(i,j-1) \rightarrow (i,j)\)

建图之后跑一个最短路就行了

// by SkyRainWind
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 3e5 + 5;

int n, s, t;
vector<pii>g[maxn];

int idx(int p,int q){return (p-1)*(n+1) + q;}
void add(int x,int y,int v){g[x].push_back(mpr(y,v));}

void add1(int x1,int y1,int x2,int y2,int v){
	if(x1 == 0){
		add(s,idx(x2,y2),v);
		return ;
	}
	if(x2 == n+1){
		add(idx(x1,y1),t,v);
		return ;
	}
	add(idx(x1,y1), idx(x2,y2), v);
}

void add2(int x1,int y1,int x2,int y2,int v){
	if(x1 == n+1){
		add(t,idx(x2,y2),v);
		return ;
	}
	if(x2 == 0){
		add(idx(x1,y1),s,v);
		return ;
	}
	add(idx(x1,y1), idx(x2,y2), v);
}

void add3(int x1,int y1,int x2,int y2,int v){
	if(y2 == 0){
		add(idx(x1,y1),t,v);
		return ;
	}
	if(y1 == n+1){
		add(s,idx(x2,y2),v);
		return ;
	}
	add(idx(x1,y1), idx(x2,y2), v);
}

void add4(int x1,int y1,int x2,int y2,int v){
//	debug();
	if(y1 == 0){
		add(t,idx(x2,y2),v);
		return ;
	}
	if(y2 == n+1){
		add(idx(x1,y1),s,v);
		return ;
	}
	add(idx(x1,y1), idx(x2,y2), v);
}

struct iedges{int val,num;bool operator<(const iedges a)const{return a.val<val;}iedges(){}iedges(int vv,int nu):val(vv),num(nu){}};
priority_queue<iedges>pq;

int vis[maxn],dis[maxn];
void dijkstra(int be){
	int N=(n+1)*(n+1)+3;
	for(int i=1;i<=N;i++)dis[i]=inf;
	dis[be]=0;
	
	pq.push(iedges(0,be));
	while(!pq.empty()){
		int fr=pq.top().num;pq.pop();
		if(vis[fr])continue;
		vis[fr]=1; 
		for(pii now : g[fr]){
			int u=now.first,v=now.second;
			if(dis[fr]+v<dis[u]){
				dis[u]=dis[fr]+v;
				pq.push(iedges(dis[u],u));
			}
		}
	}
	
	printf("%d\n",dis[t]);
}

signed main(){
	scanf("%d",&n);
	s = (n+1) * (n+1) + 1; t = s + 1;
	for(int i=1;i<=n+1;i++){	// w -> e
		for(int j=1;j<=(n);j++){
			int v;scanf("%d",&v);
			// (i,j) -> (i,j+1)  dual (i-1,j) -> (i,j)
			add1(i-1,j,i,j,v);
		}
	}
	for(int i=1;i<=n;i++){	// n -> s
		for(int j=1;j<=n+1;j++){
			int v;scanf("%d",&v);
			// (i,j) -> (i+1,j) dual (i,j) -> (i,j-1)
			add3(i,j,i,j-1,v);
		}
	}
	for(int i=1;i<=n+1;i++){	// e -> w
		for(int j=1;j<=(n);j++){
			int v;scanf("%d",&v);
			// (i,j+1) -> (i,j)  dual (i,j) -> (i-1,j)
			add2(i,j,i-1,j,v);
		}
	}
	for(int i=1;i<=n;i++){	// s -> n
		for(int j=1;j<=n+1;j++){
			int v;scanf("%d",&v);
			// (i+1,j) -> (i,j) dual (i,j-1) -> (i,j)
			add4(i,j-1,i,j,v);
//			debug();
		}
	}
	dijkstra(s);

	return 0;
}
posted @ 2022-11-22 19:34  SkyRainWind  阅读(17)  评论(0编辑  收藏  举报