BZOJ 2502: 清理雪道 有源汇上下界最小流

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

2011福建集训

analysis

本来想到的模型也就是 D A G DAG DAG 的最小路径覆盖了。

不过建图比较难受,因为我拿一般方法建图,屏幕调试都调不出来,便含泪看题解了。

于是发现这是道有源汇上下界最小流,这么说,我就明白了,不过看出来了就有些显然了,不好解释。

不过建图求解方式就明显了:

  1. 求原图 s s → t t ss\to tt sstt的最大流。
  2. 连边 s → t s\to t st,边权为 I N F INF INF
  3. s s → t t ss\to tt sstt的最大流。
  4. 答案即为边 t → s t\to s ts I N F INF INF的实际流量。

最后你可以拍着胸脯说道:这就是道模板题!(除了我)。

code

#include
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;
}

你可能感兴趣的:(网络流,======图论=======,luogu,#,BZOJ,OJ)