【NOI2010】海拔
如果點權大於,我們把它降爲,貢獻一定小。
可以同樣推出點權下界爲,就把點權壓縮在到之間了。
如果在點權全爲的情況下將其更改爲 ~ 之間的實數,則貢獻一定增大
因爲原圖是穩定在貢獻和最小的情況下的,且每個點增加或減少所帶來的貢獻度變化值正負情況一定是不變的,爲了保證貢獻值最小,該點權一定會取到極限情況,也就是和
所以分可以暴力了。
但仔細想想,和會形成一條分界線,分便可枚舉分界線。(據說是)
但分又貌似是最小割(最大流)。
但最大流強大的複雜度爲O(V2E) ,而本題中,導致時間複雜度爲三次方級別。
所以正解:最短路
如果要將左上角到右下角切斷,可以視爲有一條從左下角到右上角的貫穿矩陣的線,由多條跨越流路的線鏈接而成(多條割線),如果將方格作爲點,跨越流路的的割作爲路徑,就將最小割問題轉化爲從左下角外圍方格到右上角外圍方格的最短路問題,用上堆優化的,複雜度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;
}