二分图模板题。
输入N表示盒子总数,M表示关系数
接下来M行表示A盒子可以放在B盒子内部,也就是说盒子谁放在谁里的关系是一对一的。
既然要求放在外面的盒子最少,那么就应该尽量使每个盒子都尽量找到一个比自己大的并且放进去。
又因为不能有超过两个盒子并列放在一个盒子内部,也就是说这种找比自己体积大的盒子容量的关系是一对一的。
简单来说就是某个盒子只能配对一个比自己大的,并且保证尽量每个盒子都能找到!
这就是一个二分图最大匹配问题。和找男女朋友一样的。
匈牙利算法,有机会就上,没有机会创造机会也要上!
#include<stdio.h>///二分图最大匹配
#include<string.h>
int match[505];
bool edge[505][505],vis[505];///edge存图,表示在i是否能装下j盒子,vis标记是否询问过某个点
int n,k;
bool KM(int x)///DFS求增广路(求每个盒子所能对应的匹配,能被谁装进去)
{
for(int i=1;i<=n;i++)///遍历能装下x编号的盒子
{
if(!vis[i]&&edge[x][i])
{
vis[i]=true;///标记访问点
if(match[i]==0||KM(match[i]))
{
match[i]=x;///更新配对关系
return true;
}
}
}
return false;///若遍历了所有点仍未找到盒子装下,这个盒子裸露在外
}
int main()
{
while(scanf("%d%d",&n,&k)!=EOF)
{
int x,y;
memset(edge,false,sizeof(edge));
memset(match,0,sizeof(match));
for(int i=0;i<k;i++)
{
scanf("%d%d",&x,&y);
edge[x][y]=true;///这里x盒子能被y盒子装下,不表示y盒子能被x盒子装下,也就是有向图
}
int sum=0;
for(int i=1;i<=n;i++)
{
memset(vis,false,sizeof(vis));
KM(i);
}
for(int i=1;i<=n;i++)if(match[i]==0)sum++;///最后计数没有被装下,没有配对的盒子个数
printf("%d\n",sum);
}
}
match数组用于记录配对关系,也就是match[i]表示第i号盒子装了几号盒子。一开始大家都是空的用0表示,然后DFS为每个盒子配对,通过遍历能装下自己的盒子来搜索能和自己配对的盒子(不是能装下就一定配对了的)。对于每个盒子i,我们询问所有能装下自己的盒子,询问过的盒子做标记。表示这个盒子问过了,我已经为其找到了配对,而对于一个当前盒子中没有询问过的盒子,我将问他能不能装我,一种情况是,他谁都没装,那装我没问题!直接配对,第二种情况,他已经装了别的盒子,那么我让这个别的盒子再去寻找其他能装别的盒子的盒子,如果找到了,我将被我问的这个盒子装进去。
这个vis询问标记是对于每个盒子而言的,也就是说我1号盒子问过了2号盒子,那么1将不再问2(废话),即使间接的也不能再问(间接是指在DFS过程中可能会让别的盒子再去问2),因为这样会无限递归。
而对于盒子3,3没问过2,就可以询问。
最后统计match中为0盒子个数,也就是没有装盒子的盒子,这就是最终没有被装的盒子的数量。(想想,层层包裹的盒子作为一个集合,这个盒子中只有一个没装盒子,也只有一个没被盒子装,没装盒子的盒子match为0,根据这个特点找到0的个数,即是集合的个数,也是裸露在外的盒子个数)