题意:m个人在一条宽度为w的河的南岸,现在要到对岸,已知河中有n块石头,每块石头同时只能容纳ci个人,每个人一次都可以跳向距离为d的距离,每次跳跃耗时为1,问m个人全部过河所要花费的最少时间。
解法:不难看出此题具有网络流的各个因素。源-南岸,汇-北岸,中间点-石头,边-距离小于d的石头之间的连线,费用-1,但是m个人可以分多次过河且可以在中间节点停留,因此不可用费用流解法。由于不同时刻两点之间的流量不同,因此不能用一个网络表示整个过程。
动态流的两个基本问题是:“给出时间限制求最大流量”和“给出流量,求从源到汇输送的最小时间”,对于此类问题,我们需要建立封层网络,随着时间的推移在网络中添加新边,在残余网络中继续寻找增广路求解增加流量,由于如存在方案则最多m+n时间内完成,因此只需枚举时间,逐层添加网络来求解最小时间。因为流量限制在点上而不在边上,因此要通过拆点控制每个点的流量限制。
构图:对于时间k增加的网络:如果点i到南岸的距离小于等于d,则连(S,in(i,k),inf),如果i到北岸的距离小于等于d(out(i,k),T,inf),如果i和j距离小于d则连(out(i,k),in(j,k+1),inf)表示从t-1到t时刻的流量变化,边没有流量限制;连(in(i,k),out(i,k),c[i])限制每个时刻每块石头上最大流量。
import java.util.Arrays; import java.util.Scanner; public class River438 { int inf = 1<<28,maxn=10110,maxm=550010; class Sap { class node { int be, ne; int cap, flow; node(int be, int ne, int cap) { this.be = be; this.ne = ne; this.cap = cap; } } int E[]=new int[maxn], len,n; int h[]=new int[maxn], vh[]=new int[maxn], s, t; node buf[]=new node[maxm]; void init(int n) { this.n=n; len = 0; Arrays.fill(E, -1); } void addcap(int i, int j, int cap1, int cap2) { buf[len] = new node(j, E[i], cap1); E[i] = len++; buf[len] = new node(i, E[j], cap2); E[j] = len++; } int sap(int index, int maxcap) { if (index == t) return maxcap; int k = maxcap, d, minh = n; // 此次残余流量,某次使用流量,邻居的最小流量 for (int i = E[index]; i != -1; i = buf[i].ne) { node no = buf[i]; if (no.cap - no.flow > 0) { if (h[index] == h[no.be] + 1) { d = sap(no.be, Math.min(k, no.cap - no.flow)); // 下次找到的流量 no.flow += d; buf[i ^ 1].flow -= d;// 记录流量变化 k -= d; if (h[s] == n || k == 0)// GAP return maxcap - k; } minh = Math.min(minh, h[no.be] + 1);// 更新h[index] } } if (k == maxcap) {// 没有找到增广路 vh[minh]++; vh[h[index]]--; if (vh[h[index]] == 0) h[s] = n; h[index] = minh; } return maxcap - k; } int solve(int s, int t) { if (s == t) return inf; this.s = s;this.t = t; Arrays.fill(h, 0); Arrays.fill(vh, 0); // for (int i = 0; i < len; i++) // buf[i].flow = 0; int ans = 0; while (h[s] != n) ans += sap(s, inf); return ans; } } Sap sp=new Sap(); boolean near(int i,int j){ if(i==j) return false; return d*d>=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]); } int x[]=new int[maxn],y[]=new int[maxn],c[]=new int[maxn]; Scanner scan=new Scanner(System.in); int n,m,d,w,s=10001,t=10002; void run(){ n=scan.nextInt(); m=scan.nextInt(); d=scan.nextInt(); w=scan.nextInt(); for(int i=1;i<=n;i++) { x[i]=scan.nextInt(); y[i]=scan.nextInt(); c[i]=scan.nextInt(); } if(d>=w){ System.out.println(1); return; } sp.init(2); int cnt=0,i; for(i=1;i<=m+n;i++) { build(i); cnt+=sp.solve(s,t); if(cnt>=m) break; } if(cnt>=m) System.out.println(i+1); else System.out.println("IMPOSSIBLE"); } int in(int i,int t){ t--; return t*n*2+i; } int out(int i,int t){ t--; return t*n*2+i+n; } void build(int k){ sp.n+=n*2; for(int i=1;i<=n;i++){ if(y[i]<=d) sp.addcap(s,in(i,k),m,0); if(y[i]+d>=w) sp.addcap(out(i,k),t,m,0); sp.addcap(in(i,k),out(i,k),c[i],0); for(int j=1;j<=n;j++) if(near(i,j)) sp.addcap(out(i,k),in(j,k+1),m,0); } } public static void main(String[] args) { new River438().run(); } }