观察题目,总的代价是由各个节点的海拔决定的,而s与t都已事先确定好了(废话,不然玩什么),我们需要做的就是确定各个点的海拔。
思考后发现,由左上到右下海拔的趋势是逐渐升高的,不会出现高-->低-->高,或者低-->高-->低的情况,因为这样会支付两边来人的钱,得不偿失。
继续观察,发现由a到b,如果海拔增高就一定会一次性从0增到1,因为当出现h[a]<h[b]<h[c] 逐渐升高的情况时,假设w[a,b]>w[b,c],则这样设置海拔的代价会大于h[a]==h[b]==0,h[c]=1的情况。
所以图中节点的海拔非0即1,且左上那一团为0,右下那一团为1。(可见他给的四舍五入,保留到整数有多坑)
思考到这里我们就发现,我们要寻找的就是那一条01节点接壤的线,而这条线正好将这些点分为了两个集合,s与t也恰好在这两个集合,so,瞬间想到最小割。
然而存在20%的数据图中有250000个点,这么多点用网络流坑定会超时,有想到这个图是一个规则的图,再联想到bzoj1001狼抓兔子,可以把最小割转化为对偶图的最短路,而最短路不论是spfa还是heap_dij都可以在2s内跑完。
注:如何转化为对偶图比较简单,很容易YY出来。
有一点太坑了,要注意转化为对偶图之后边的数量,我wa了两次,不晓得为什么,还是把数据下下来才反应过来的,太2了,虐杀呀。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX=1000000;
int n;
int s,t;
int tot=1;
int fir[300000],en[MAX*2],nex[MAX*2],w[MAX*2];
inline void ins(int a,int b,int c){
// printf("%d %d %d\n",a,b,c);
nex[++tot]=fir[a];
fir[a]=tot;
en[tot]=b;
w[tot]=c;
}
int dis[300000];
int sta[MAX*24+10],v[300000];
inline void spfa(){
memset(dis,31,sizeof(dis));
dis[s]=0;
int head=0,tair=1;
sta[1]=s;v[s]=1;
while (head<tair){
head++;
// if (head>=MAX*10) head=1;
for (int k=fir[sta[head]];k;k=nex[k])
if (dis[en[k]]>dis[sta[head]]+w[k]){
dis[en[k]]=dis[sta[head]]+w[k];
if (!v[en[k]]){
tair++;
// if (tair>=MAX*10) tair=1;
sta[tair]=en[k];
v[en[k]]=1;
}
}
v[sta[head]]=0;
}
}
int main(){
// freopen("altitude.in","r",stdin);
// freopen("altitude.out","w",stdout);
scanf("%d",&n);
s=0;t=n*n+1;
//西to东
for (int j=1;j<=n;j++){
int x;
scanf("%d",&x);
ins(j,t,x);
}
for (int i=2;i<=n;i++)
for (int j=1;j<=n;j++){
int x;
scanf("%d",&x);
ins((i-1)*n+j,(i-2)*n+j,x);
}
for (int j=1;j<=n;j++){
int x;
scanf("%d",&x);
ins(s,n*(n-1)+j,x);
}
//北to南
for (int i=1;i<=n;i++){
int x;
scanf("%d",&x);
ins(s,(i-1)*n+1,x);
for (int j=2;j<=n;j++){
scanf("%d",&x);
ins((i-1)*n+j-1,(i-1)*n+j,x);
}
scanf("%d",&x);
ins(i*n,t,x);
}
//东to西
for (int j=1;j<=n;j++){
int x;
scanf("%d",&x);
ins(t,j,x);
}
for (int i=2;i<=n;i++)
for (int j=1;j<=n;j++){
int x;
scanf("%d",&x);
ins((i-2)*n+j,(i-1)*n+j,x);
}
for (int j=1;j<=n;j++){
int x;
scanf("%d",&x);
ins(n*(n-1)+j,s,x);
}
//南to北
for (int i=1;i<=n;i++){
int x;
scanf("%d",&x);
ins((i-1)*n+1,s,x);
for (int j=2;j<=n;j++){
scanf("%d",&x);
ins((i-1)*n+j,(i-1)*n+j-1,x);
}
scanf("%d",&x);
ins(t,i*n,x);
}
spfa();
printf("%d",dis[t]);
return 0;
}