sgu 438 The Glorious Karlutka River =)(動態網絡流)

題意: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();
	}
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章