【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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章