POJ-2771 匈牙利算法+最大独立集(还有其他的概念链接) 链式前向星

Guardian of Decency

 

Frank N. Stein是一位非常保守的高中教师。他想带一些学生参加短途旅行,但他担心他们中的一些人可能会成为情侣。虽然你永远不能排除这种可能性,但他已经制定了一些他认为表明两个人成为一对夫妇的概率很低的规则: 

  • 它们的高度相差超过40厘米。 
  • 他们属于同一性别。 
  • 他们喜欢的音乐风格不同。 
  • 他们最喜欢的运动是相同的(他们可能是不同球队的球迷,这将导致战斗)。


因此,对于他带来短途旅行的任何两个人,他们必须满足至少一个上述要求。根据他们的重要信息,帮助他找到他可以服用的最多人数。 

输入

输入的第一行包含一个整数T≤100,给出了测试用例的数量。每个测试用例的第一行包含一个N≤500的整数,给出了学生的数量。接下来,每个学生将有一行由四个以空格分隔的数据项组成: 

  • 一个整数h,以cm为单位给出高度; 
  • 女性角色'F'或男性角色'M'; 
  • 描述首选音乐风格的字符串; 
  • 一个带有喜爱运动名称的字符串。


输入中的字符串不会包含超过100个字符,任何字符串也不会包含任何空格。 

产量

对于输入中的每个测试用例,应该有一行带有一个整数,给出最大数量的合格学生。

这题就直接翻译的,勉强还能看(对二分图及匈牙利实现不太理解的看这里

这道题说是要求最大的不可能谈恋爱的人数,一开始我不知道二分图里面还有最大独立集这个概念,结果直接求最大匹配数,总是和答案不一样,在这里要知道这个:

最大独立集=n-最大匹配数(二分图的一些概念+几个证明

最大独立集,是两两之间没有连线的点的集合,在这道题目中是不能谈恋爱的不就是两人之间没有牵线的吗,因此这道题跑一个二分图最大的能够谈恋爱的最大匹配数,然后用节点数(就是总人数)减去就好了

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define rep(i,a,b)  for(int i=a;i<=b;i++)
int k,m,n;
bool e[600][600];
bool vis[600];
int link[600];
int ans;
struct node
{
    int h;
    char sex;
    char style[150];
    char sport[150];
}num[550];
bool dfs(int x)
{
    rep(i,1,n)
    {
        if(e[x][i]&&!vis[i])
        {
            vis[i]=1;
            if(link[i]==0||dfs(link[i]))
            {

                link[i]=x;
                return true;
            }
        }
    }
    return false;
}
void dig()
{
    ans=0;
    memset(link ,0,sizeof link);
    rep(i,1,n)
    {
        memset(vis,0,sizeof vis);
        if(dfs(i))
             ans++;
    }
}
bool check(int a,int b)
{
    if((abs(num[a].h-num[b].h)>40)||(num[a].sex==num[b].sex)||(strcmp(num[a].style,num[b].style)!=0||(strcmp(num[a].sport,num[b].sport)==0)))
       return true;
       return false;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        memset(e,0,sizeof e);
        memset(num,0,sizeof num);
            int x,y;
        rep(i,1,n){
        scanf("%d%*c",&num[i].h);
        scanf("%c%s%s",&num[i].sex,num[i].style,num[i].sport);
        }
        rep(i,1,n)
        rep(j,1,n)
        {
            if(i!=j&&!check(i,j))
                e[i][j]=1;
        }
        dig();
        printf("%d\n",n-ans/2);//在这里处理的是双向图了,所以要除以2
    }
}

开始我以为只要输出有几个不谈恋爱的最大匹配不就行了,但是那样有重复的点,因为这题是让你输出都多少位同学是不谈恋爱的,如果你这样求就错了,举个栗子

样例第一个是1--2   1--3    1--4   2--3没有恋爱关系,如果你输出只用不恋爱的最大匹配数,那答案就是2(ans要除以2,是双向图),这显然是不对的,不恋爱的人数是3对吧,这是点不是对,所以这样就不能AC了。

 

下面是用链式前向星

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define rep(i,a,b)  for(int i=a;i<=b;i++)
int k,m,n;
//bool e[600][600];
bool vis[600];
int link[600];
int ans;
struct Edge
{
    int to;
    int next;
}q[2500000];
int head[505];
int tot;
struct node
{
    int h;
    char sex;
    char style[150];
    char sport[150];
}num[550];
bool dfs(int x)
{
    for(int i=head[x];i!=0;i=q[i].next)
    {
        int k=q[i].to;
        if(!vis[k])
        {
            vis[k]=1;
            if(link[k]==0||dfs(link[k]))
            {
                link[k]=x;
                return true;
            }
        }
    }
    return false;
}
void dig()
{
    ans=0;
    memset(link ,0,sizeof link);
    rep(i,1,n)
    {
        memset(vis,0,sizeof vis);
        if(dfs(i))
             ans++;
    }
}
bool check(int a,int b)
{
    if((abs(num[a].h-num[b].h)>40)||(num[a].sex==num[b].sex)||(strcmp(num[a].style,num[b].style)!=0||(strcmp(num[a].sport,num[b].sport)==0)))
       return true;
       return false;
}
void add(int i,int j)
{
    q[tot].to=j;
    q[tot].next=head[i];
    head[i]=tot++;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        tot=1;
        scanf("%d",&n);
        memset(q,0,sizeof q);
        memset(num,0,sizeof num);
        memset(head,0,sizeof head);
            int x,y;
        rep(i,1,n){
        scanf("%d%*c",&num[i].h);
        scanf("%c%s%s",&num[i].sex,num[i].style,num[i].sport);
        }
        rep(i,1,n)
        rep(j,1,n)
        {
            if(i!=j&&!check(i,j))
              add(i,j);
        }
        dig();
        printf("%d\n",n-ans/2);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值