BZOJ1001 BJOI2006狼抓兔子(最小割+最短路)

时间:2022-04-04 08:13:51

  显然答案就是最小割。直接跑dinic也能过,不过显得不太靠谱。

  考虑更正确的做法。作为一个平面图,如果要把他割成两半,那么显然可以用一条曲线覆盖且仅覆盖所有割边。于是我们把空白区域看成点,隔开他们的边看成边,原图的最小割就是这张新图中能割开原起点和终点的两个区域之间的最短路。

  建出来的新图就是原图的对偶图。平面图最小割=对偶图最短路。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
#define N 1010
#define S 2000000
#define T 2000001
int n,m,d[N*N<<],p[N*N<<],t=;
struct data{int to,nxt,len;
}edge[N*N*];
bool flag[N*N<<];
struct point
{
int x,d;
bool operator <(const point&a) const
{
return d>a.d;
}
};
priority_queue<point> q;
int trans(int x,int y,int p)
{
if (x==) return S;
if (x==n) return T;
if (y==) return T;
if (y==m) return S;
return ((x-)*(m-)+(y-)<<)+p;
}
void addedge(int x,int y,int z)
{
t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].len=z,p[x]=t;
t++;edge[t].to=x,edge[t].nxt=p[y],edge[t].len=z,p[y]=t;
}
void dijkstra()
{
q.push((point){S,});
memset(d,,sizeof(d));d[S]=;
for (int i=;i<=trans(n,m,)+;i++)
{
while (!q.empty()&&flag[q.top().x]) q.pop();
if (q.empty()) break;
point k=q.top();q.pop();
flag[k.x]=;
for (int j=p[k.x];j;j=edge[j].nxt)
if (k.d+edge[j].len<d[edge[j].to])
{
d[edge[j].to]=k.d+edge[j].len;
q.push((point){edge[j].to,d[edge[j].to]});
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj1001.in","r",stdin);
freopen("bzoj1001.out","w",stdout);
const char LL[]="%I64d";
#else
const char LL[]="%lld";
#endif
n=read(),m=read();
for (int i=;i<=n;i++)
for (int j=;j<m;j++)
{
int x=read();
addedge(trans(i-,j,),trans(i,j,),x);
}
for (int i=;i<n;i++)
for (int j=;j<=m;j++)
{
int x=read();
addedge(trans(i,j-,),trans(i,j,),x);
}
for (int i=;i<n;i++)
for (int j=;j<m;j++)
{
int x=read();
addedge(trans(i,j,),trans(i,j,),x);
}
dijkstra();
cout<<d[T];
return ;
}