【BZOJ1001】狼抓兔子(平面图最小割转最短路)

时间:2021-10-02 13:28:38

题意:有一张平面图,求它的最小割。N,M.表示网格的大小,N,M均小于等于1000.

左上角点为(1,1),右下角点为(N,M).有以下三种类型的道路 
1:(x,y)<==>(x+1,y) 
2:(x,y)<==>(x,y+1) 
3:(x,y)<==>(x+1,y+1) 
 
思路:第一眼看就是一个最小割=最大流,但点数1000000,边数6000000过大
所以要平面图最小割转最短路 详情见周驿东《浅析最大最小定理在信息学竞赛中的应用》
n=1和m=1要特判,链状的我也母鸡为什么会错,特判就对了
 var q:array[..]of longint;
dis,head:array[..]of longint;
inq:array[..]of boolean;
vet,next,len:array[..]of longint;
num:array[..,..,..]of longint;
n,m,i,j,x,tot,st,ed,s,ans:longint; procedure add(a,b,c:longint);
begin
inc(tot);
next[tot]:=head[a];
vet[tot]:=b;
len[tot]:=c;
head[a]:=tot;
end; function min(x,y:longint):longint;
begin
if x<y then exit(x);
exit(y);
end; procedure spfa;
var t,w,e,v,u:longint;
begin
fillchar(dis,sizeof(dis),$1f);
fillchar(inq,sizeof(inq),false);
t:=-; w:=; q[]:=st; dis[st]:=; inq[st]:=true;
while t<w do
begin
inc(t); u:=q[t mod (ed+)];
inq[u]:=false;
e:=head[u];
while e<> do
begin
v:=vet[e];
if dis[u]+len[e]<dis[v] then
begin
dis[v]:=dis[u]+len[e];
if not inq[v] then
begin
inc(w); q[w mod (ed+)]:=v;
inq[v]:=true;
end;
end;
e:=next[e];
end;
end;
writeln(dis[ed]);
end; begin
//assign(input,'bzoj1001.in'); reset(input);
//assign(output,'bzoj1001.out'); rewrite(output);
readln(n,m);
if (n=)and(m=) then
begin
writeln();
exit;
end;
if n= then
begin
ans:=maxlongint;
for i:= to m- do begin read(x); ans:=min(ans,x); end;
writeln(ans);
exit;
end;
if m= then
begin
ans:=maxlongint;
for j:= to n- do begin read(x); ans:=min(ans,x); end;
writeln(ans);
exit;
end; for i:= to n do
for j:= to m do
begin
inc(s); num[i,j,]:=s;
inc(s); num[i,j,]:=s;
end;
inc(s);
st:=s; ed:=s+;
for i:= to n do
for j:= to m- do
begin
read(x);
if i= then
begin
add(st,num[i,j,],x);
add(num[i,j,],st,x);
end
else if i=n then
begin
add(ed,num[i-,j,],x);
add(num[i-,j,],ed,x);
end
else
begin
add(num[i-,j,],num[i,j,],x);
add(num[i,j,],num[i-,j,],x);
end;
end;
for i:= to n- do
for j:= to m do
begin
read(x);
if j= then
begin
add(num[i,j,],ed,x);
add(ed,num[i,j,],x);
end
else if j=m then
begin
add(num[i,j-,],st,x);
add(st,num[i,j-,],x);
end
else
begin
add(num[i,j-,],num[i,j,],x);
add(num[i,j,],num[i,j-,],x);
end;
end;
for i:= to n- do
for j:= to m- do
begin
read(x);
add(num[i,j,],num[i,j,],x);
add(num[i,j,],num[i,j,],x);
end;
spfa;
// close(input);
//close(output);
end.

UPD(2018.10.17):C++

 #include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef vector<int> VI;
#define fi first
#define se second
#define MP make_pair
#define N 2100000
#define M 6100000
#define eps 1e-8
#define pi acos(-1)
#define oo 1e9 int num[][][],q[N],dis[N],head[N],inq[N],
vet[M],nxt[M],len[M],tot,s,st,ed; int add(int a,int b,int c)
{
nxt[++tot]=head[a];
vet[tot]=b;
len[tot]=c;
head[a]=tot;
} void spfa(int st,int ed)
{
memset(dis,0x3f,sizeof(dis));
memset(inq,,sizeof(inq));
int t=; int w=; q[]=st; dis[st]=; inq[st]=;
while(t<w)
{
t++; int u=q[t%(s+)];
inq[u]=;
int e=head[u];
while(e)
{
int v=vet[e];
if(dis[u]+len[e]<dis[v])
{
dis[v]=dis[u]+len[e];
if(!inq[v])
{
w++; q[w%(s+)]=v;
inq[v]=;
}
}
e=nxt[e];
}
}
printf("%d\n",dis[ed]);
} int main()
{
//freopen("bzoj1001.in","r",stdin);
//freopen("bzoj1001.out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
if(n==&&m==){printf("0\n"); return ;}
if(n==)
{
int ans=oo;
for(int i=;i<m;i++)
{
int x;
scanf("%d",&x);
ans=min(ans,x);
}
printf("%d\n",ans);
return ;
}
if(m==)
{
int ans=oo;
for(int i=;i<n;i++)
{
int x;
scanf("%d",&x);
ans=min(ans,x);
}
printf("%d\n",ans);
return ;
}
s=;
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
for(int k=;k<=;k++) num[i][j][k]=++s;
int st=++s;
int ed=++s;
for(int i=;i<=n;i++)
for(int j=;j<=m-;j++)
{
int x;
scanf("%d",&x);
if(i==)
{
add(st,num[i][j][],x);
add(num[i][j][],st,x);
}
else if(i==n)
{
add(ed,num[i-][j][],x);
add(num[i-][j][],ed,x);
}
else
{
add(num[i-][j][],num[i][j][],x);
add(num[i][j][],num[i-][j][],x);
}
} //横边
for(int i=;i<=n-;i++)
for(int j=;j<=m;j++)
{
int x;
scanf("%d",&x);
if(j==)
{
add(num[i][j][],ed,x);
add(ed,num[i][j][],x);
}
else if(j==m)
{
add(num[i][j-][],st,x);
add(st,num[i][j-][],x);
}
else
{
add(num[i][j-][],num[i][j][],x);
add(num[i][j][],num[i][j-][],x);
}
} //纵边 for(int i=;i<=n-;i++)
for(int j=;j<=m-;j++)
{
int x;
scanf("%d",&x);
add(num[i][j][],num[i][j][],x);
add(num[i][j][],num[i][j][],x);
} //斜边
spfa(st,ed);
return ;
}