双子树(BSOI1105)

双子树

Description

【问题描述】
  这一天,随身带来的干粮豆吃玩了,必须有人去找些食物来吃才行。可Z4的懒惰是出了名的,谁会愿意去呢?经过一番商讨,大家最后决定以老规矩“猜十次”的方式决定人选。可怜的立方由于猜拳技术不过关,屡战屡败,只好踏上觅食的征途。
  立方后来找到了两棵奇怪的树,由于它们的形态十分相像,双子座的立方一时兴起便把它们命名为“双子树”。可双子树的果实能不能吃呢?“管它呢,先摘了再说。”于是身手敏捷的立方便爬上其中一棵,摘下一个大大的果实。可就在此时,另一棵却有几个果实坠地。
  立方细心一看,发现这双子树上的某些果实有一些细丝相连,只要摘下其中一棵树的某一个果实,另一棵树将会有相应的一个或多个(也可能没有)果实坠地而摔坏,这些果实都和摘下的果实用细丝相连。摔坏的果实当然不能吃了。而且,立方发现,那些细丝是没办法弄断的,除非摘下它两端的果实的其中一个。由于这时只有立方一人(好没义气的Z4啊……),他只能眼睁睁地看着它们摔坏。也就是说,立方无法同时摘取任一条细丝两端上的两个果实。于是,不同的摘法最后会得到不同数量的果实,而立方将会用随身带的一个容量为V(表示能装V个果实)的大袋子将它们装好。
  馋嘴的立方当然希望摘得越多越好啦,那么,他最多可以得到多少个果实呢?
  特别地,任两个果实间最多只会有一条细丝相连,同一棵树上的果实间不会有细丝相连,当袋子装满后,立方的口还可以塞进一个。

Input

  输入文件tree.in的第一行包括四个整数V(0<=V<=1000),N1,N2(0<=N1,N2<=500)和M,分别表示袋子的容量,第一棵树上的果实个数,第二棵树上的果实个数和细丝总数。为了方便计算,立方人为地把果实分别按1..N1和1..N2标号。
  接下来有M行,每行有两个整数A,B(1<=A<=N1,1<=B<=N2),表示第一棵树上的果实A和第二棵树上的果实B有一条细丝相连。

Output

  输出文件tree.out仅有一个整数,表示立方最多能得到的果实个数。

Sample Input

10 3 3 5

1 2

2 1

2 2

2 3

3 2

Sample Output

4

Hint

【输入样例2】
  2 4 4 4
  1 1
  2 2
  3 3
  4 4
【输出样例2】
  3

【样例说明】:
  样例1中,立方最后摘取了第一棵树上的1、3和第二棵树上的1、3。
  样例2中,立方本来最多可以摘4个果实,但由于袋子容量仅为2,再加上他口中的一个,最后他只能得到3个。

Solution

最大覆盖网络流版本,全部的果子数减去最小割和V+1(口袋。。。)取min即可。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int inf=0x3f3f3f3f;
inline int read(){
	char c;int rec=0;
	while((c=getchar())<'0'||c>'9');
	while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
	return rec;
}
int v,a,b,m;
struct Hose{int next,to,flow;}hose[500005];
int h[500005],cnt=1;
inline void add(int x,int y,int z){
	hose[++cnt].to=y;hose[cnt].next=h[x];h[x]=cnt;hose[cnt].flow=z;
	hose[++cnt].to=x;hose[cnt].next=h[y];h[y]=cnt;hose[cnt].flow=0;
	return ;
}
int st,ed,tot,ans=0,N;
int Gap[500005],dis[500005];
inline int SAP(int v,int maxflow){
	if(v==ed)return maxflow;
	int temp=maxflow,i,j,p;
	for(i=h[v];i;i=hose[i].next){
		j=hose[i].to;
		if(hose[i].flow&&dis[v]==dis[j]+1){
			p=SAP(j,min(hose[i].flow,temp));
			temp-=p;hose[i].flow-=p;hose[i^1].flow+=p;
			if(temp==0||dis[st]==N)return maxflow-temp;
		}
	}
	if(--Gap[dis[v]]==0)dis[st]=N;
	else Gap[++dis[v]]++;
	return maxflow-temp;
}
int main(){
	v=read();a=read();b=read();m=read();v++;
	tot=a+b;st=0;ed=tot+1;N=ed+1;
	int i,x,y,z;
	for(int i=1;i<=a;i++)add(st,i,1);
	for(int i=1;i<=b;i++)add(i+a,ed,1);
	for(int i=1;i<=m;i++){
		x=read();y=read();
		add(x,y+a,1);
	}
	Gap[0]=N;
	while(dis[st]<N)ans+=SAP(st,inf);
	cout<<min(v,tot-ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值