这道题目居然可以转化为最小割。。实际上只要剥去题目中华(keng)丽(die)的外壳,就是一个裸的最小割。
首先,这道题目中的终点一定是最高点。不妨考虑所有比终点高的点,将这些点的海拔都变为1,那么答案显然变小,同理起点一定是最低点,因此所有点的海拔范围为[0,1]。
其次,需要剥去这道题目最大的一个外壳,也就是海拔为小数这个绚(keng)丽(die)的条件。实际上一个点的海拔不是0就是1。对于一个点,显然可以将它调整使它至少和周围的一个等高(这类似于在数轴上找一个点使其到给定的N个点的距离与点权乘积的和最小,则这个点一定在某一个给定的点处)。这样经过不断调整就会出现一种局面:即所有点分为两个联通块,一块全为0,包括起点;一块全为1,包括中点。这样,问题就转化为最小割了(即两个联通块之间的路径需要被切断,即割)。
于是就变成经典的平面图最小割转对偶图最短路了,详细的可以去网上搜索bzoj1001的题解,原理是一样的。
听说spfa会被卡,不过好像也有写过的。算了打个winnner tree不用stl里面的堆作弊了(真无聊)。
AC代码如下:
#include<iostream> #include<cstdio> #include<cstring> #define inf 1000000000 #define N 1300005 using namespace std; int n,tot,c[N][2],val[N],fst[N],pnt[N],len[N],nxt[N],d[N],p[505][505]; bool vis[N]; int read(){ int x=0; char ch=getchar(); while (ch<'0' || ch>'9') ch=getchar(); while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } return x; } void add(int aa,int bb,int cc){ pnt[++tot]=bb; len[tot]=cc; nxt[tot]=fst[aa]; fst[aa]=tot; } void build(int k,int l,int r){ c[k][0]=l; c[k][1]=r; if (l==r) val[k]=inf; else{ int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); val[k]=inf; } } void ins(int k,int x,int y){ int l=c[k][0],r=c[k][1],mid=(l+r)>>1; if (l==r){ val[k]=y; return; } if (x<=mid) ins(k<<1,x,y); else ins(k<<1|1,x,y); val[k]=min(val[k<<1],val[k<<1|1]); } int qry(int k){ if (c[k][0]==c[k][1]) return c[k][0]; else if (val[k]==val[k<<1]) return qry(k<<1); else return qry(k<<1|1); } int solve(int sta,int gol){ int i,p; build(1,sta,gol); for (i=sta; i<=gol; i++) d[i]=inf; d[sta]=0; ins(1,sta,0); for (i=0; i<=gol; i++){ int x=qry(1); d[x]=val[1]; if (x==gol) return d[x]; ins(1,x,inf); vis[x]=1; for (p=fst[x]; p; p=nxt[p]){ int y=pnt[p]; if (vis[y]) continue; if (d[x]+len[p]<d[y]){ d[y]=d[x]+len[p]; ins(1,y,d[y]); } } } return d[gol]; } int main(){ n=read(); int i,j,cnt=0; for (i=1; i<=n; i++) for (j=1; j<=n; j++) p[i][j]=++cnt; cnt++; for (i=1; i<=n; i++) p[0][i]=p[i][n+1]=cnt; for (i=1; i<=n+1; i++) for (j=1; j<=n; j++) add(p[i][j],p[i-1][j],read()); for (i=1; i<=n; i++) for (j=1; j<=n+1; j++) add(p[i][j-1],p[i][j],read()); for (i=1; i<=n+1; i++) for (j=1; j<=n; j++) add(p[i-1][j],p[i][j],read()); for (i=1; i<=n; i++) for (j=1; j<=n+1; j++) add(p[i][j],p[i][j-1],read()); printf("%d\n",solve(0,cnt)); return 0; }
by lych
2016.1.22