title
BZOJ 2502
LUOGU 4843
Description
滑雪场坐落在FJ省西北部的若干座山上。
从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向。
你的团队负责每周定时清理雪道。你们拥有一架直升飞机,每次飞行可以从总部带一个人降落到滑雪场的某个地点,然后再飞回总部。从降落的地点出发,这个人可以顺着斜坡向下滑行,并清理他所经过的雪道。
由于每次飞行的耗费是固定的,为了最小化耗费,你想知道如何用最少的飞行次数才能完成清理雪道的任务。
Input
输入文件的第一行包含一个整数n (2 <= n <= 100) – 代表滑雪场的地点的数量。接下来的n行,描述1~n号地点出发的斜坡,第i行的第一个数为mi (0 <= mi < n) ,后面共有mi个整数,由空格隔开,每个整数aij互不相同,代表从地点i下降到地点aij的斜坡。每个地点至少有一个斜坡与之相连。
Output
输出文件的第一行是一个整数k – 直升飞机的最少飞行次数。
Sample Input
8
1 3
1 7
2 4 5
1 8
1 8
0
2 6 5
0
Sample Output
4
Source
analysis
本来想到的模型也就是 \(DAG\) 的最小路径覆盖了。
不过建图比较难受,因为我拿一般方法建图,屏幕调试都调不出来,便含泪看题解了。
于是发现这是道有源汇上下界最小流,这么说,我就明白了,不过看出来了就有些显然了,不好解释。
不过建图求解方式就明显了:
- 求原图 \(ss\to tt\)的最大流。
- 连边 \(s\to t\),边权为 \(INF\)。
- 求 \(ss\to tt\)的最大流。
- 答案即为边 \(t\to s\),\(INF\)的实际流量。
最后你可以拍着胸脯说道:这就是道模板题!(除了我)。
code
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10,maxm=1e6+10,inf=0x3f3f3f3f;
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
template<typename T>inline void write(T x)
{
if (!x) { putchar('0'); return ; }
if (x<0) putchar('-'), x=-x;
T num=0, ch[20];
while (x) ch[++num]=x%10+48, x/=10;
while (num) putchar(ch[num--]);
}
int ver[maxm<<1],edge[maxm<<1],Next[maxm<<1],head[maxn],len=1;
inline void add(int x,int y,int z)
{
ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
ver[++len]=x,edge[len]=0,Next[len]=head[y],head[y]=len;
}
int ans,M[maxn];
inline void insert(int x,int y,int up,int low)
{
add(x,y,up-low);
if (low) M[y]+=low,M[x]-=low;
}
int s,t,ss,tt;
int dist[maxn];
inline bool bfs()
{
queue<int>q;
memset(dist,0,sizeof(dist));
q.push(s),dist[s]=1;
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=head[x]; i; i=Next[i])
{
int y=ver[i];
if (edge[i] && !dist[y])
{
dist[y]=dist[x]+1;
if (y==t) return 1;
q.push(y);
}
}
}
return 0;
}
inline int get(int x,int low)
{
if (x==t) return low;
int tmp=low;
for (int i=head[x]; i; i=Next[i])
{
int y=ver[i];
if (edge[i] && dist[y]==dist[x]+1)
{
int a=get(y,min(tmp,edge[i]));
if (!a) dist[y]=0;
edge[i]-=a;
edge[i^1]+=a;
if (!(tmp-=a)) break;
}
}
return low-tmp;
}
int main()
{
int n,ans=0;
read(n);
ss=0,tt=n+1,s=tt+1,t=s+1;
for (int i=1,mi; i<=n; ++i)
{
insert(ss,i,inf,0);
insert(i,tt,inf,0);
read(mi);
for (int j=1,x; j<=mi; ++j) read(x),insert(i,x,inf,1);
}
for (int i=1; i<=tt; ++i)
if (M[i]>0) add(s,i,M[i]);
else add(i,t,-M[i]);
while (bfs()) while (get(s,inf));
insert(tt,ss,inf,0);
while (bfs()) ans+=get(s,inf);
write(ans);
return 0;
}