【BZOJ1143】祭祀(网络流)

【BZOJ1143】祭祀(网络流)

题面

BZOJ
洛谷

Description

  在遥远的东方,有一个神秘的民族,自称Y族。他们世代居住在水面上,奉龙王为神。每逢重大庆典, Y族都

会在水面上举办盛大的祭祀活动。我们可以把Y族居住地水系看成一个由岔口和河道组成的网络。每条河道连接着

两个岔口,并且水在河道内按照一个固定的方向流动。显然,水系中不会有环流(下图描述一个环流的例子)。

img

  由于人数众多的原因,Y族的祭祀活动会在多个岔口上同时举行。出于对龙王的尊重,这些祭祀地点的选择必

须非常慎重。准确地说,Y族人认为,如果水流可以从一个祭祀点流到另外一个祭祀点,那么祭祀就会失去它神圣

的意义。族长希望在保持祭祀神圣性的基础上,选择尽可能多的祭祀的地点。

Input

第一行包含两个用空格隔开的整数N、M,分别表示岔口和河道的数目,岔口从1到N编号。

接下来M行,每行包含两个用空格隔开的整数u、v,

描述一条连接岔口u和岔口v的河道,水流方向为自u向v。

N≤100M≤1000

Output

第一行包含一个整数K,表示最多能选取的祭祀点的个数。

Sample Input

4 4
1 2
3 4
3 2
4 2

Sample Output

2
【样例说明】
在样例给出的水系中,不存在一种方法能够选择三个或者三个以上的祭祀点。包含两个祭祀点的测试点的方案有两种:
选择岔口1与岔口3(如样例输出第二行),选择岔口1与岔口4。
水流可以从任意岔口流至岔口2。如果在岔口2建立祭祀点,那么任意其他岔口都不能建立祭祀点
但是在最优的一种祭祀点的选取方案中我们可以建立两个祭祀点,所以岔口2不能建立祭祀点。对于其他岔口
至少存在一个最优方案选择该岔口为祭祀点,所以输出为1011。

题解

最长反链覆盖

看到这道题目的第一眼的思路
就觉得很类似于最小路径覆盖
但是我们发现这样是错的,
因为无法确定路径的联通关系
现在稍微改一下就行了,既然只要是联通的就不能选了
那么就把有边直接相连,改为是否联通
提前跑\(Floyd\)处理一下再连边就行了
(二分图匹配也是可以的)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 555
#define INF 1000000000
inline int read()
{
    int x;scanf("%d",&x);return x;
}
struct Line{int v,next,w;}e[MAX*MAX*4];
int h[MAX],cnt=2;
inline void Add(int u,int v,int w)
{
	e[cnt]=(Line){v,h[u],w};h[u]=cnt++;
	e[cnt]=(Line){u,h[v],0};h[v]=cnt++;
}
int level[MAX],S,T;
bool bfs()
{
	memset(level,0,sizeof(level));level[S]=1;
	queue<int> Q;Q.push(S);
	while(!Q.empty())
	{
		int u=Q.front();Q.pop();
		for(int i=h[u];i;i=e[i].next)
			if(e[i].w&&!level[e[i].v])level[e[i].v]=level[u]+1,Q.push(e[i].v);
	}
	return level[T];
}
int dfs(int u,int flow)
{
	if(u==T||!flow)return flow;
	int ret=0;
	for(int i=h[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(e[i].w&&level[v]==level[u]+1)
		{
			int d=dfs(v,min(flow,e[i].w));
			ret+=d;flow-=d;
			e[i].w-=d;e[i^1].w+=d;
		}
	}
	if(!ret)level[u]=0;
	return ret;
}
int Dinic()
{
	int ret=0;
	while(bfs())ret+=dfs(S,INF);
	return ret;
}
int n,m,g[MAX][MAX];
int main()
{
	n=read();m=read();
	S=0;T=n+n+1;
	for(int i=1;i<=m;++i)g[read()][read()]=1;
	for(int k=1;k<=n;++k)
		for(int i=1;i<=n;++i)
			for(int j=1;j<=n;++j)
				g[i][j]|=g[i][k]&g[k][j];
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			if(g[i][j])Add(i,j+n,1);
	for(int i=1;i<=n;++i)Add(S,i,1),Add(i+n,T,1);
	printf("%d\n",n-Dinic());
	return 0;
}

posted @ 2018-04-03 10:24  小蒟蒻yyb  阅读(297)  评论(0编辑  收藏  举报