题目描述
由于人类对自然资源的消耗,人们意识到大约在 2300 年之后,地球就不能再居住了。于是在月球上建立了新的绿地,以便在需要时移民。令人意想不到的是,2177 年冬由于未知的原因,地球环境发生了连锁崩溃,人类必须在最短的时间内迁往月球。
现有 nn 个太空站位于地球与月球之间,且有 mm 艘公共交通太空船在其间来回穿梭。每个太空站可容纳无限多的人,而太空船的容量是有限的,第 ii 艘太空船只可容纳 h_ihi 个人。每艘太空船将周期性地停靠一系列的太空站,例如 (1,3,4)(1,3,4) 表示该太空船将周期性地停靠太空站 134134134\dots134134134…。每一艘太空船从一个太空站驶往任一太空站耗时均为 11。人们只能在太空船停靠太空站(或月球、地球)时上、下船。
初始时所有人全在地球上,太空船全在初始站。试设计一个算法,找出让所有人尽快地全部转移到月球上的运输方案。
输入格式
输入的第一行是三个用空格隔开的整数,分别代表太空站个数 nn,太空船个数 mm 和地球上的人数 kk。
第 22 到第 (m + 1)(m+1) 行,每行给出一艘太空船的信息,第 (i + 1)(i+1) 行的第一个整数 h_ihi 代表第 ii 艘太空船可容纳的人数。随后有一个整数 r_iri,代表第 ii 艘太空船停靠的站点数。之后有 r_iri 个整数,依次代表该太空船停靠站点的编号 S_{i, j}Si,j,其中太空站自 11 至 nn 编号,地球编号为 00,月球编号为 -1−1。
输出格式
输出一行一个整数,代表将所有人转移到月球上的最短用时。若无解则输出 00。
输入输出样例
输入 #1复制
2 2 1
1 3 0 1 2
1 3 1 2 -1
输出 #1复制
5
说明/提示
数据规模与约定
对于 100\%100% 的数据,保证:
- 1 \leq n \leq 131≤n≤13。
- 1 \leq m \leq 201≤m≤20。
- 1 \leq k \leq 501≤k≤50。
- 1 \leq r_i \leq n + 21≤ri≤n+2。
- -1 \leq S_{i, j}\leq n−1≤Si,j≤n。
---------是页-----------太-------------长---------------了---------------口巴--------------
思路:因为不同飞船每天到站情况不一样,所以要对每一天分层,每个太空站在新一天的编号要和前一天的编号连边(因为人可以选择在一个太空站多带几天),在该天可以通过飞船连通的太空站之间要连边,再有就是每一天的地球之间要连边。
kuangbin的模板好像不太行的样子emm也许我还妹调出来
dinic需要用当前弧优化。
建图框架:https://www.luogu.com.cn/blog/Qiu/solution-p2754
代码参考:https://blog.csdn.net/aiwo1376301646/article/details/104398423(有个很好看的图
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e4 + 5;
const int M = 1e5 + 5;
const int inf = 0x3f3f3f3f;
int n, m, k, s, t, tot, head[N], cur[N], dep[N];
int a[25], mp[25][25], p[25];//用上了分层图,可以用dep判重了
struct edge
{
int to, next, w;
}e[M];
void init() {
tot = 1;
memset(head, -1, sizeof(head));
}
void addedge(int x,int y,int w)
{
e[++tot].to=y;e[tot].w=w;e[tot].next=head[x];head[x]=tot;
e[++tot].to=x;e[tot].w=0;e[tot].next=head[y];head[y]=tot;
}
bool bfs()//bool 函数是一个小优化,判断是否能搜到汇点,连汇点都搜不到就不dfs了
{
memset(dep, 0, sizeof dep);//一定要初始化
memcpy(cur, head, sizeof(head));
queue<int>q;
q.push(s);dep[s]=1;
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].next)
{
int y=e[i].to,w=e[i].w;
if(w&&!dep[y])//如果有残余流量(没有的话谁也过不去)并且这个点是第一次到达
{
dep[y]=dep[x]+1;
q.push(y);
}
}
}
return dep[t];//t 的深度不为0,就是搜到了汇点
}
int dfs(int u,int flow) {
if(u==t) return flow;
int ans=0;
for(int i=cur[u];i&&ans<flow;i=e[i].next) {
cur[u]=i;
int v=e[i].to;
if(e[i].w&&dep[v]==dep[u]+1) {
int x=dfs(v,min(e[i].w,flow-ans));
if(x) e[i].w-=x,e[i^1].w+=x,ans+=x;
}
}
if(ans<flow) dep[u]=-1;//说明这个点已经榨干
return ans;
}
int main() {
init();
int v, cnt, b;
scanf("%d%d%d", &n, &m, &k);
s = 0, t = n + 1, cnt = t;
int g = n + 2;
for(int i = 1; i <= m; ++i) {
scanf("%d%d", &p[i], &a[i]);
for(int j = 0; j < a[i]; ++j) {
scanf("%d", &v);
mp[i][j] = (v != -1 ? v : t);
}
}
int day = 0, ans = 0;
while(true) {
if(day == 27 && ans == 0) {
printf("%d\n", ans);
break;
}
for(int i = 0; i <= n; ++i) {
++cnt;
addedge(cnt - g, cnt, inf);
}
++cnt;
addedge(cnt, cnt - g, inf);
for(int i = 1; i <= m; ++i) {
addedge(mp[i][day % a[i]] + day * g, mp[i][(day + 1) % a[i]] + (day + 1) * g, p[i]);
}
++day;
while(bfs()) ans += dfs(s, inf);
if(ans >= k) {
printf("%d\n", day);
break;
}
}
return 0;
}