hdu(1498和2119)气球,最小点覆盖

本文探讨了一种使用二分图匹配解决特定矩阵覆盖问题的方法,通过建立矩阵元素之间的联系,并运用ED算法求解二分最大匹配,从而最小化覆盖所有元素所需的步骤。文章还提供了实例代码实现,帮助理解如何将理论应用到实际编程中。

大意:给你一个0/1矩阵,以及一把枪,一枪可以打掉同一行或者同一列上的1,问你最少几次可以把1全部变为0?

思路:我们以x,y轴为顶点建立二分图,如果数字为1的话,那么就连一条边,其实就转换成了最小顶点覆盖的问题,然后用ED算法求二分最大匹配即可.

注意:

1、最大的匹配顶点数应该为n,所以for(int i = 1; i <= n; i++) ED(i);

2、矩阵数据范围是1~100,所以最大的边数应该是100*100才对,否则RE.

#include<stdio.h>

#include<string.h>

int map[1000][1000];

int link[1000],mark[1000];

int m,n;

int find(int k)//////就处理一种一种颜色,所以一个变量,下面要两个。。、

{

    int i;

    for(i=1;i<=m;i++)

    {

        if(mark[i]==0&&map[k][i])

        {

            mark[i]=1;

            if(link[i]==0||find(link[i]))

            {

                link[i]=k;

                return 1;

            }

        }

    }

    return 0;

}

int main()

{

    int i,j,k,sum;

    while(scanf("%d",&n),n)

    {

        scanf("%d",&m);

        memset(map,0,sizeof(map));

        memset(link,0,sizeof(link));

        for(i=1;i<=n;i++)

            for(j=1;j<=m;j++)

            {

                scanf("%d",&k);

                if(k==1)

                    map[i][j]=1;

            }

            sum=0;

            for(i=1;i<=n;i++)

            {

                memset(mark,0,sizeof(mark));

                if(find(i))

                    sum++;

            }

            printf("%d\n",sum);

    }

    return 0;

}

大家可以先做一下hdu 2119,每次只能删除一行或一列的1,求的是最少多少次能把1删除完,以行为一个子集,列为一个子集,如果map[i][j]=1就是ij建一条边,把1删除完,就是每条边都要被删除,就是最小点覆盖问题,这一题也是每次只能消除一行或一列的相同颜色的气球,要求出删除每一种颜色的气球最少需要多少次,如果大于k的话就不能删除完,要求的就是不能被删除完的。每一种气球就相当于hdu 21191,也是求最小点覆盖。。



1498

题意:给你一个n*n的矩阵,在矩阵中分布着s种颜色的气球,给你k次扎破气球的操作,每次操作可以扎破一行,或一列的同一颜色的气球。问在k次操作后有哪几种颜色的气球是不能被完全扎破的。

解题:

利用二分图匹配,寻找每一种颜色对应的最大匹配(行和列分别为A集合,B集合;M[i,j]代表一个搭配),如果大于k则输出“-1”,否则输出颜色的递增序列。

注:在二分图中求最少的点,让每条边都至少和其中的一个点关联,这就是二分图的“最小顶点覆盖”。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
 
int map[101][101];

int color[51],link[101];

int mark[101],p[51];

int n;

int cmp(const void*a,const void *b)

{

    return *(int*)a-*(int*)b;

}

int find(int k,int h)

{

    int i;

    for(i=1;i<=n;i++)

    {

        if(mark[i]==0&&map[k][i]==h)//相同颜色的才能匹配(重点)

        {

            mark[i]=1;

            if(link[i]==0||find(link[i],h))----find两个量。。

            {

                link[i]=k;

                return 1;

            }

        }

    }

    return 0;

}

int main()

{

    int i,j,m,k,h,sum;

    while(scanf("%d%d",&n,&m),n||m)

    {

        k=1;

        memset(map,0,sizeof(map));

        memset(mark,0,sizeof(mark));

        memset(color,0,sizeof(color));

        for(i=1;i<=n;i++)

            for(j=1;j<=n;j++)

            {

                scanf("%d",&map[i][j]);

                if(mark[map[i][j]]==0)

                {

                    mark[map[i][j]]=1;

                    color[k]=map[i][j];//颜色的种数

                    k++;

                }

            }

            h=0;

            for(i=1;i<=k;i++)/求每种颜色被删除的最小次数(((每一种操作和上面对一处理的方式一样,这里多种颜色,所以要多次处理))每次都要初始化))

            {

                memset(link,0,sizeof(link));

                sum=0;

                for(j=1;j<=n;j++)

                {

                    memset(mark,0,sizeof(mark));

                    sum+=find(j,color[i]);

                }

                if(sum>m)

                {

                    p[h]=color[i];/统计不能被删除的颜色

                    h++;

                }

            }

            qsort(p,h,sizeof(p[0]),cmp);排序

            if(h==0)

                printf("-1\n");

            else

            {

                for(i=0;i<h-1;i++)

                    printf("%d ",p[i]);

                printf("%d\n",p[h-1]);

            }

    }

    return 0;

}

            


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值