这一天,随身带来的干粮豆吃玩了,必须有人去找些食物来吃才行。可Z4的懒惰是出了名的,谁会愿意去呢?经过一番商讨,大家最后决定以老规矩“猜十次”的方式决定人选。可怜的立方由于猜拳技术不过关,屡战屡败,只好踏上觅食的征途。
立方后来找到了两棵奇怪的树,由于它们的形态十分相像,双子座的立方一时兴起便把它们命名为“双子树”。可双子树的果实能不能吃呢?“管它呢,先摘了再说。”于是身手敏捷的立方便爬上其中一棵,摘下一个大大的果实。可就在此时,另一棵却有几个果实坠地。
立方细心一看,发现这双子树上的某些果实有一些细丝相连,只要摘下其中一棵树的某一个果实,另一棵树将会有相应的一个或多个(也可能没有)果实坠地而摔坏,这些果实都和摘下的果实用细丝相连。摔坏的果实当然不能吃了。而且,立方发现,那些细丝是没办法弄断的,除非摘下它两端的果实的其中一个。由于这时只有立方一人(好没义气的Z4啊……),他只能眼睁睁地看着它们摔坏。也就是说,立方无法同时摘取任一条细丝两端上的两个果实。于是,不同的摘法最后会得到不同数量的果实,而立方将会用随身带的一个容量为V(表示能装V个果实)的大袋子将它们装好。
馋嘴的立方当然希望摘得越多越好啦,那么,他最多可以得到多少个果实呢?
特别地,任两个果实间最多只会有一条细丝相连,同一棵树上的果实间不会有细丝相连,当袋子装满后,立方的口还可以塞进一个。
输入格式:
输入文件dobtree.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有一条细丝相连。
输出格式:
输出文件dobtree.out仅有一个整数,表示立方最多能得到的果实个数。
样例输入:
10 3 3 5 1 2 2 1 2 2 2 3 3 2
样例输出:
4
数据范围:
样例1中,立方最后摘取了第一棵树上的1、3和第二棵树上的1、3。
样例2中,立方本来最多可以摘4个果实,但由于袋子容量仅为2,再加上他口中的一个,最后他只能得到3个。
时间限制:
1
空间限制:
256M
解题分析:
就是一个裸的konig定理,就能够解决问题。
从样例中发现,这些细丝不具有传递性。即若摘下A树甲果通过细丝影响到相连的B树乙果掉下,但不会通过乙果的细丝又传回到A树中与乙果相连的C果掉下。
很显然,A树和B树及细线组成了二分图。
根据题意,A树和B树有细线相连的两个果子,不管如何摘, 至少要掉下一只。设本图最大匹配数为K,考虑到最大匹配中的每一对匹配都互相独立的,因此,至少要掉K只果子。
另一方面,我们考察能否寻找到只掉K个果子的摘果方法。我们从A树考察开始摘果子甲果, 如果甲果是未匹配的果子,则影响B树的掉下的乙果一定是匹配了的果子,否则存在交错轨了。接着可推出与B树乙果相匹配的A树丙果,可以摘到。A树丙果不可能受到相连的B树摘下未匹配的丁果而掉下,因为这样又存在“甲-乙-丙-丁”交叉轨了。如果我们在A树摘下的是匹配的果子,则B树与它匹配的果子掉下。因此,我们全部摘下A树中未匹配的果子,只引起B树部分匹配的果子掉下,与这些果子匹配的A树果子保住了,全部摘下B树中未匹配的果子,只引起A树部分匹配的果子掉下,与这些果子匹配的A树果子保住了,A树与B树的一对匹配果子不可能都掉下,否则这对匹配果子都连上一个未匹配的果子,这就是交叉轨,当然不可能。
综上分析,按上面摘法,所有未匹配的果子都可摘下,匹配的一对果子都能摘下一个,因此,存在摘法只掉K只果子。因引,最多可以得到果子数为果子总数-最大匹配数K。
显然答案是MIN(果子总数-最大匹配数K,袋子容量+1)。
算法复杂度为O(n^2)。
//双子树问题,THIS IS A MARICLE;
#include<bits/stdc++.h>
using
namespace
std;
int
n1,n2,v,m,d[1001],ans;
//d records it -> what in another tree;
bool
used[1001],way[1001][1001];
void
init()
{
for
(
int
i=1;i<=m;i++)
{
int
x,y;
cin>>x>>y;
way[x][y]=1;
}
}
bool
find(
int
x)
{
if
(used[x])
return
0;
for
(
int
i=1;i<=n2;i++)
{
if
(way[x][i])
{
used[x]=1;
//find will use it!!!
if
(d[i]==0||find(d[i]))
{
d[i]=x;
return
1;
}
}
}
return
0;
}
int
main()
{
cin>>v>>n1>>n2>>m;
v++;
init();
for
(
int
i=1;i<=n1;i++)
{
for
(
int
k=1;k<=n1;k++)
{
used[k]=0;
}
if
(find(i)) ans++;
}
ans=n1+n2-ans;
cout<<min(ans,v);
}