[FJOI2020]世紀大逃亡 題解
阿新 • • 發佈:2020-07-16
FJOI2020 D1T1
題目大意
給出一個由 $n$ 行 $m$ 列的點構成的網格,其中第 $1$ 行,第 $n$ 行,第 $1$ 列與第 $m$ 列為邊界,給出 $s$ 個點,求這 $s$ 個點到邊界的最小的路徑長度之和,要求路徑不能交叉。 $n*m\leq20000$。
思路分析
可以看出這是個費用流模板題。根據實測,本題資料卡 EK 單路增廣,需要多路增廣才能過。雖然多路增廣理論複雜度相同,但是本題中每次增廣只能增加 $1$ 的流量,多路增廣的確可以提高一定的效率。
#include<iostream> #include<cstdio> #include<queue> #define ano ((i-1)^1)+1 using namespace std; const int N=1e6+100,INF=0x7f7f7f7f; int n,m,S,tot,s,t,f,ans; int head[N],ver[2*N],Next[2*N],edge[2*N],cost[2*N]; int minf[N],pre[N],d[N]; bool v[N]; void add(int x,int y,int z,int c) { ver[++tot]=y,edge[tot]=z,cost[tot]=c,Next[tot]=head[x],head[x]=tot; ver[++tot]=x,edge[tot]=0,cost[tot]=-c,Next[tot]=head[y],head[y]=tot; } bool spfa() { for(int i=0;i<=t;i++) d[i]=INF,v[i]=0; queue<int> q; q.push(s); v[s]=1,d[s]=0,minf[s]=INF; while(q.size()) { int x=q.front();q.pop();v[x]=0; for(int i=head[x];i;i=Next[i]) { if(!edge[i]) continue; int y=ver[i]; if(d[x]+cost[i]<d[y]) { d[y]=d[x]+cost[i]; minf[y]=min(minf[x],edge[i]); pre[y]=i; if(!v[y]) { v[y]=1; q.push(y); } } } } return d[t]!=INF; }/* void update() { int x=t; while(x!=s) { int i=pre[x]; edge[i]-=minf[t]; edge[ano]+=minf[t]; x=ver[ano]; } f+=minf[t]; ans+=d[t]*minf[t]; }*///單路增廣 int dinic(int x,int flow) { if(x==t) return flow; v[x]=1; int rest=flow,use; for(int i=head[x];i && rest;i=Next[i]) { int y=ver[i]; if(v[y] || !edge[i] || d[y]!=d[x]+cost[i]) continue; use=dinic(y,min(rest,edge[i])); if(!use) d[y]=0; edge[i]-=use,edge[ano]+=use,rest-=use; ans+=cost[i]*use; } return flow-rest; } int id(int x,int y,int z) { return (x-1)*m+y+z*n*m; } void clear() { t=2*n*m+1,ans=tot=f=0; for(int i=0;i<=t;i++) head[i]=pre[i]=minf[i]=0; } int main() { //freopen("covid.in", "r", stdin); //freopen("covid.out", "w", stdout); while(scanf("%d%d%d",&n,&m,&S)!=EOF) { clear(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { add(id(i,j,0),id(i,j,1),1,0); if(i!=1) add(id(i,j,1),id(i-1,j,0),1,1); if(i!=n) add(id(i,j,1),id(i+1,j,0),1,1); if(j!=1) add(id(i,j,1),id(i,j-1,0),1,1); if(j!=m) add(id(i,j,1),id(i,j+1,0),1,1); if(i==1 || i==n || j==1 || j==m) add(id(i,j,1),t,1,0); } for(int i=1,x,y;i<=S;i++) { scanf("%d%d",&x,&y); add(s,id(x,y,0),1,0); }/* while(spfa()) update();*///單路增廣 while(spfa()) { int flow; do{ for(int i=0;i<=t;i++) v[i]=0; flow=dinic(s,INF); f+=flow; }while(flow); } if(f==S) printf("%d\n",ans); else puts("-1"); } return 0; }