【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
1 3 1 2 –1
样例输出 Sample Output
5
数据范围及提示 Data Size & Hint
1<=m<=13, 1<=n<=20, 1<=k<=50。