[NOI2010]海拔(最小割)

来源:互联网 发布:mysql计算重复数据 编辑:程序博客网 时间:2024/04/29 01:57

【题解】

先寻找每个点的最优高度范围:
首先每个点的海拔不可能比周围四个点都高或都低,然后设某点高度为x,假设围四个点高度已知,随便考虑几种情况,可以发现:总体力值关于x单调(增或减)
因此可以得出结论:每个点的海拔都与周围的某个点相同 

=> 可以推出(我是猜出)每个点高度都是0或1,且按高度标记每个点后,图上只有2个连通块 


然后就是熟悉的最小割转最短路了,给每个域编号后建新图,要注意建的有向边的方向(见图)


比如:规定两个上下相邻、"期望海拔方案"为上0下1的点的连边,对应新图中的边的方向为:从右到左 
          两个左右相邻、"期望海拔方案"为左0右1的点的连边,对应新图中的边的方向为:从上到下 


【代码】

#include<stdio.h>#include<stdlib.h>typedef long long LL;LL w[2000000]={0},d[3000000]={0},heap[300000]={0};//heap[i]:堆的第i个位置对应元素的序号 int v[2000000]={0},first[300000]={0},next[2000000]={0},pos[300000]={0};//pos[i]:第i个元素在堆中的的序号,heap[]与pos[]作用相反 const LL INF=1000000000000000000ll;int e=0,node;void jh_heap(int a,int b){int t=pos[heap[a]];//"先交换pos再heap"不必须 pos[heap[a]]=pos[heap[b]];pos[heap[b]]=t;t=heap[a];heap[a]=heap[b];heap[b]=t;}void tj(int x,int y,int z){v[++e]=y;w[e]=(LL)z;next[e]=first[x];first[x]=e;}void tz(int x){int i;for(i=x;i>1;i/=2){if(d[heap[i/2]]>d[heap[i]]) jh_heap(i/2,i);else return;}}void sc(){int i=1;jh_heap(1,node);node--;while(i*2<=node){i*=2;if(i+1<=node&&d[heap[i]]>d[heap[i+1]]) i++;if(d[heap[i/2]]>d[heap[i]]) jh_heap(i/2,i);else return;}}int main(){int n,i,j,s,t,x;scanf("%d",&n);s=n*n+1;t=s+1;for(i=1;i<=n+1;i++)//西->东(左0右1),从上到下建边 for(j=1;j<=n;j++){scanf("%d",&x);if(i==1) tj(s,j,x);if(i>1&&i<=n) tj((i-2)*n+j,(i-1)*n+j,x);if(i>n) tj((i-2)*n+j,t,x);}for(i=1;i<=n;i++)//北->南(上0下1),从右到左建边 for(j=1;j<=n+1;j++){scanf("%d",&x);if(j==1) tj((i-1)*n+j,t,x);if(j>1&&j<=n) tj((i-1)*n+j,(i-1)*n+j-1,x);if(j>n) tj(s,i*n,x);}for(i=1;i<=n+1;i++)//东->西(左1右0),从下到上建边 for(j=1;j<=n;j++){scanf("%d",&x);if(i>1&&i<=n) tj((i-1)*n+j,(i-2)*n+j,x);}for(i=1;i<=n;i++)//南->北(上1下0),从左到右建边 for(j=1;j<=n+1;j++){scanf("%d",&x);if(j>1&&j<=n) tj((i-1)*n+j-1,(i-1)*n+j,x);}heap[1]=s;pos[s]=1;for(i=1;i<=n*n;i++){d[i]=INF;heap[i+1]=i;pos[i]=i+1;}d[t]=INF;node=heap[t]=pos[t]=t;for(i=1;i<t;i++){x=heap[1];sc();for(j=first[x];j!=0;j=next[j])if(d[v[j]]>d[x]+w[j]){d[v[j]]=d[x]+w[j];tz(pos[v[j]]);}}printf("%lld",d[t]);return 0;}


0 0