天天看點

【wikioi】1034 家園(最大流+特殊的技巧)

http://wikioi.com/problem/1034/

太神了這題。

其實一開始我以為是費用流,但是總感覺不對。

原因是我沒看到一句話,特定的時刻到達特定的點!!

也就是說,并不是每艘船每次都從起點到終點,是以裸的費用流肯定不行。

翻了題解。。

好恐怖,,按時間拆點。

每一時刻的太空站我們都拆一個點,然後将上一時刻的太空站向這一時刻的太空站連一條容量為oo的邊,表示在上一時刻太空站待着的人可以在這一時刻登船

然後每一時刻飛船都向對應時刻所到達的太空站連容量為飛船容量的邊。

然後每一次都跑最大流,将人數k減去最大流,當k小于0的時候,答案就是這一時刻。(經測試,時刻設為100耗時20ms,時刻設為30也能ac耗時10ms)

#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
#define rep(i, n) for(int i=0; i<(n); ++i)
#define for1(i,a,n) for(int i=(a);i<=(n);++i)
#define for2(i,a,n) for(int i=(a);i<(n);++i)
#define for3(i,a,n) for(int i=(a);i>=(n);--i)
#define for4(i,a,n) for(int i=(a);i>(n);--i)
#define CC(i,a) memset(i,a,sizeof(i))
#define read(a) a=getint()
#define print(a) printf("%d", a)
#define dbg(x) cout << #x << " = " << x << endl
#define printarr(a, n, m) rep(aaa, n) { rep(bbb, m) cout << a[aaa][bbb]; cout << endl; }
inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; }
inline const int max(const int &a, const int &b) { return a>b?a:b; }
inline const int min(const int &a, const int &b) { return a<b?a:b; }

const int N=5010, M=2000000, oo=~0u>>1, s=5000, t=s+1;
int ihead[N], cnt=1, d[N], p[N], cur[N], gap[N], n, m, k, S[25], H[25], a[25][30];
struct ED { int from, to, cap, w, next; } e[M];
inline void add(const int &u, const int &v, const int &c) {
	e[++cnt].next=ihead[u]; ihead[u]=cnt; e[cnt].to=v; e[cnt].from=u; e[cnt].cap=c;
	e[++cnt].next=ihead[v]; ihead[v]=cnt; e[cnt].to=u; e[cnt].from=v; e[cnt].cap=0;
}
inline void update(const int &T) {
	for1(i, 0, n+1) add(i+(T-1)*22, i+T*22, oo);
	int u, v;
	for1(i, 1, m) {
		u=(T-1)*22+a[i][(T-1)%S[i]];
		v=T*22+a[i][T%S[i]];
		add(u, v, H[i]);
	}
}
int isap(const int &s, const int &t, const int &n) {
	for1(i, 0, t) cur[i]=ihead[i];
	CC(d, 0); CC(gap, 0);
	int ret=0, i, f, u=s;
	gap[0]=n;
	while(d[s]<n) {
		for(i=cur[u]; i; i=e[i].next) if(e[i].cap && d[u]==d[e[i].to]+1) break;
		if(i) {
			p[e[i].to]=cur[u]=i; u=e[i].to;
			if(u==t) {
				for(f=oo; u!=s; u=e[p[u]].from) f=min(f, e[p[u]].cap);
				for(u=t; u!=s; u=e[p[u]].from) e[p[u]].cap-=f, e[p[u]^1].cap+=f;
				ret+=f;
			}
		}
		else {
			if(! (--gap[d[u]]) ) break;
			d[u]=n; cur[u]=ihead[u];
			for(i=ihead[u]; i; i=e[i].next) if(e[i].cap && d[u]>d[e[i].to]+1) d[u]=d[e[i].to]+1;
			++gap[d[u]];
			if(u!=s) u=e[p[u]].from;
		}
	}
	return ret;
}
int main() {
	read(n); read(m); read(k);
	for1(i, 1, m) {
		read(H[i]); read(S[i]);
		rep(j, S[i]) {
			read(a[i][j]);
			if(a[i][j]==-1) a[i][j]=n+1;
		}
	}
	for1(i, 0, 100) add(s, i*22, oo), add((n+1)+i*22, t, oo);
	for1(i, 1, 100) {
		update(i);
		k-=isap(s, t, t);
		if(k<=0) { print(i); return 0; }
	}
	print(0);
	return 0;
}
      

題目描述 Description

由于人類對自然的瘋狂破壞,人們意識到在大約2300年之後,地球不能再居住了,于是在月球上建立了新的綠地,以便在需要時移民。令人意想不到的是,2177年冬由于未知的原因,地球環境發生了連鎖崩潰,人類必須在最短的時間内遷往月球。

現 有n個太空站處于地球與月球之間(編号1..n),m艘公共交通太空梭在其中來回穿梭,每個太空站Si可容納無限的人,每艘太空梭pi隻可容納Hpi人。 對于每一艘太空梭pi,将周期性地停靠一系列的太空站(Si1,Si2…Sir),如:(1,3,4)表示停靠太空站1 3 4 1 3 4 1 3 4 …。 任一艘太空梭從任一個太空站駛往另一個任意的太空站耗時為1。人隻能在太空梭停靠太空站(或地球、月球)時上船或下船。初始時的人全在地球上,太空梭全在 初始站(太空梭pi處于Si1),目标是讓所有的人盡快地全部轉移到月球上。

輸入描述 Input Description

檔案第一行為三個正整數 n(太空站個數)、 m(太空梭個數)、 k(需要運送的地球上的人的個數),其中 1<=m<=13, 1<=n<=20, 1<=k<=50。

接 下來的n行給出了太空梭的資訊,第i+1行說明太空梭pi,此行第一個數表示pi可容納的人數Hpi,第二個數表示pi停靠一個周期的太空站個數 r,1<=r<=n+2, 随後r個數便是停靠的太空站的編号(Si1,Si2,…,Sir), 地球用0表示,月球用-1表示。0時刻時,所有太空梭都在初始站,随後開始運作,在時刻1,2,3…等正點時刻各艘太空梭停靠相應的太空站,即人隻有在 0,1,2…等正點時刻才能上下太空梭。

輸出描述 Output Description

檔案隻有一個數,若問題有解,輸出完成全部人員安全轉移的時刻,否則輸出0。

樣例輸入 Sample Input

2 2 1 

1 3 0 1 2

樣例輸出 Sample Output

資料範圍及提示 Data Size & Hint

本文為部落客原創文章,未經部落客允許不得轉載。一經發現,必将追究法律責任。