Description
滑雪场坐落在FJ省西北部的若干座山上。
从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向。
你的团队负责每周定时清理雪道。你们拥有一架直升飞机,每次飞行可以从总部带一个人降落到滑雪场的某个地点,然后再飞回总部。从降落的地点出发,这个人可以顺着斜坡向下滑行,并清理他所经过的雪道。
由于每次飞行的耗费是固定的,为了最小化耗费,你想知道如何用最少的飞行次数才能完成清理雪道的任务。
2 <= n <= 100
Solution
考虑直接网络流要怎么搞。对于入度为0的点连源点,出度为0的点连汇点,斜坡就连费用为[1,INF]的边
注意到我们要求的是一个有上下界的最小流,然后我就学习了一波上下界网络流( ̄▽ ̄)”
(下面的各种流都是有上下界的网络流)
建图方法:
新建超级源点汇点SS和TT,对于原图中的一条边(x,y,[l,r])我们连(SS,y,l),(TT,x,l),(x,y,r-l)
若是有源汇的图就多连一条(T,S,INF),其余同上
存在可行流当且仅当SS的边全部满流
最小流可以二分答案mid,把S和T的连边变为(T,S,mid)跑可行流判断
最大流同理
最小费用流直接在边上加权,其他不变
Code
#include <stdio.h>
#include <string.h>
#include <queue>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
#define copy(x,t) memcpy(x,t,sizeof(x))
const int INF=0x3f3f3f3f;
const int N=2005;
const int E=400005;
std:: queue <int> que;
struct edge {int x,y,w,next;} e[E];
int dis[N],d[N],S,T,SS,TT;
int cur[N],ls[N],edCnt=1;
void add_edge(int x,int y,int w) {
e[++edCnt]=(edge) {x,y,w,ls[x]}; ls[x]=edCnt;
e[++edCnt]=(edge) {y,x,0,ls[y]}; ls[y]=edCnt;
}
bool bfs(int st,int ed) {
for (;!que.empty();) que.pop();
que.push(st);
fill(dis,0); dis[st]=1;
for (;!que.empty();) {
int now=que.front(); que.pop();
for (int i=ls[now];i;i=e[i].next) {
if (e[i].w>0&&!dis[e[i].y]) {
dis[e[i].y]=dis[now]+1;
if (e[i].y==ed) return true;
que.push(e[i].y);
}
}
}
return false;
}
int find(int now,int ed,int mn) {
if (now==ed||!mn) return mn;
int ret=0;
for (int &i=cur[now];i;i=e[i].next) {
if (e[i].w>0&&dis[now]+1==dis[e[i].y]) {
int d=find(e[i].y,ed,std:: min(e[i].w,mn-ret));
e[i].w-=d; e[i^1].w+=d; ret+=d;
if (mn==ret) break;
}
}
return ret;
}
int dinic(int st,int ed) {
int ret=0;
for (;bfs(st,ed);) {
copy(cur,ls);
ret+=find(st,ed,INF);
}
return ret;
}
bool check(int mid,int n) {
for (int i=2;i<=edCnt;i+=2) {
e[i].w+=e[i^1].w; e[i^1].w=0;
}
e[edCnt^1].w=mid;
dinic(SS,TT);
bool flag=true;
for (int i=ls[SS];i;i=e[i].next) {
if (e[i].w) flag=false;
}
return flag;
}
int main(void) {
int n; scanf("%d",&n);
S=0,T=n+1,SS=n+2,TT=n+3;
rep(i,1,n) {
int s; scanf("%d",&s);
if (!s) add_edge(i,T,INF);
for (;s--;) {
int y; scanf("%d",&y); d[y]++;
add_edge(SS,y,1); add_edge(i,TT,1);
add_edge(i,y,INF-1);
}
}
rep(i,1,n) if (!d[i]) {
add_edge(S,i,INF);
}
add_edge(T,S,INF);
int l=1,r=INF;
while (l<=r) {
int mid=(l+r)>>1;
if (check(mid,n)) r=mid-1;
else l=mid+1;
}
printf("%d\n", r+1);
return 0;
}