【NOI2010】海拔

【NOI2010】海拔

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

如果点权大于11,我们把它降为11,贡献一定小。
可以同样推出点权下界为00,就把点权压缩在0011之间了。

如果在点权全为0101的情况下将其更改为00 ~ 11之间的实数,则贡献一定增大
因为原图是稳定在贡献和最小的情况下的,且每个点增加或减少所带来的贡献度变化值正负情况一定是不变的,为了保证贡献值最小,该点权一定会取到极限情况,也就是0011

所以2020分可以暴力了。

但仔细想想,0011会形成一条分界线,5050分便可枚举分界线。(据说是DPDP

8080分又貌似是最小割(最大流)。
但最大流强大的复杂度为O(V2E) ,而本题中EVE≈V,导致时间复杂度为三次方级别。

所以正解:最短路

如果要将左上角到右下角切断,可以视为有一条从左下角右上角的贯穿矩阵的线,由多条跨越流路的线链接而成(多条割线),如果将方格作为点跨越流路的的割作为路径,就将最小割问题转化为从左下角外围方格到右上角外围方格的最短路问题,用上堆优化的dijkstradijkstra,复杂度O(n2logn)

网络流转化为对偶图求最短路


推荐题解


#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
struct edge{int v,w,nxt;}e[N<<2];
int first[N],cnt=0;
inline void add(int u,int v,int w){
	e[++cnt].w=w;e[cnt].v=v;
	e[cnt].nxt=first[u];first[u]=cnt;
}
int dis[N],vis[N],s,t,n;
int dijkstra(){
	priority_queue< pair<int,int> >q;
	memset(dis,0x3f,sizeof(dis));
	dis[s]=0;
	q.push(make_pair(0,s));
	while(!q.empty()){
		int u=q.top().second;q.pop();
		if(vis[u])continue;
		vis[u]=1;
		for(int i=first[u];i;i=e[i].nxt){
			int v=e[i].v;
			if(dis[v]>dis[u]+e[i].w){
				dis[v]=dis[u]+e[i].w;
				q.push(make_pair(-dis[v],v));
			}
		}
	}
	return dis[t];//不能一搜到t就结束
}
void init(){
	scanf("%d",&n);
	int x;s=1e6-3;t=1e6-2;
	//left-->right   up-->down
	//方格作为点
	for(int i=0;i<=n;i++)
	for(int j=1;j<=n;j++){
		scanf("%d",&x);
		if(i==n)add(s,(i-1)*n+j,x);//
		else if(!i)add(j,t,x);
		else add(i*n+j,(i-1)*n+j,x);
	}
	for(int i=1;i<=n;i++)
	for(int j=0;j<=n;j++){
		scanf("%d",&x);
		if(!j)add(s,(i-1)*n+j+1,x);
		else if(j==n)add(i*n,t,x);
		else add((i-1)*n+j,(i-1)*n+j+1,x);
   		}
	for(int i=0;i<=n;i++)
	for(int j=1;j<=n;j++){
		scanf("%d",&x);
		if(i==n)add((i-1)*n+j,s,x);
		else if(!i)add(t,j,x);
		else add((i-1)*n+j,i*n+j,x);
	}
	for(int i=1;i<=n;i++)
	for(int j=0;j<=n;j++){
		scanf("%d",&x);
		if(!j)add((i-1)*n+j+1,s,x);
		else if(j==n)add(t,i*n,x);
		else add((i-1)*n+j+1,(i-1)*n+j,x);
   	}
}
int main(){
	init();
	printf("%d",dijkstra());
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章