POJ 2724

题目类型:二分图-最大边匹配的变形
解题思路:
(1)numcount(坏的cheese数)- maxmatch(此处是指被匹配的所有顶点)+maxmatch/2;其中numcount-maxmatch是没有匹配的顶点,即必须采取的操作
而maxmatch/2则是可以通过'*'来实现的操作。
(2)建边:当两个整数只有一位不相同时连接一条边
(3)重边的判断:借助数组refinal实现
建边的时候有一个小优化,就是求A,B的2进制表示是否只差1位
C=A^B;
C&&((C&(C-1))==0)的结果就表示他们是否只差1位

下面稍微证明一下
1.如果C=0,那么很明显A,B的2进制表示不可能只差1位
可是0&(-1)=0...
2.对于(1<<K)来说,在满足1的条件下(K>=0)必然有(1<<K)&((1<<K)-1)=0

 100000
&011111
=------
 000000
3.至于为什么要xor,看看下面的例子就明白了~
 10010
^10011
------
 00001

 10000
^11000
-------
 01000

#include <iostream>
#include <string.h>
//#include <conio.h>
using namespace std;
#define marray 1025     //最多有十个开关,所以是2^10,不要设成M的两倍,否则会超时
int map[marray][marray];
bool final[marray];
int match[marray];
int num[marray];
bool refinal[marray];   //加入此数组主要用于重边的判断
int n,m;
int numcount = 0;       //记录顶点的个数,即被感染的cheese的总数
char str[15]; 
bool isConnect(int c)   //判断两个整数是否只有一位不同 (位运算实现)
{
     return (c&&((c&(c-1))==0));
}
int strtoint(char * s)          //将位串转换为相应的整数
{
    int L=strlen(s),i=0,j=0;    //使用strlen,不要使用sizeof实现
    int sum=0;
    for(i=L-1;i>=0;i--,j++)     //string类型的最高位为位串的最低位
    {
        if(s[i]=='1') sum+=(1<<j); //<<是左移位操作符,右操作数j指明要移动的位数,左移相当于乘2,右移相当于除2
    }
    return sum;
}
bool DFS(int p)
{
     int i,j;
     int t,k;
     for(i=1;i<=numcount;++i)
     {
         if(map[p][i]&& !final[i])
         {
             final[i]=true;
             t = match[i];
             match[i] = p;
             if(t==0 || DFS(t)) return true;
             match[i] = t;      //注意二分匹配时不必在此处回溯时改变final的值,如果从一个点A出发,没有找到增广路径,那么无论再从别的点出发找到多少增广路径来改变现在的匹配,从A出发都永远找不到增广路径。
         }              
     }
     return false;
}
int mat()
{
    int maxmatch = 0;
    int i,j;
    for(i=1;i<=numcount;++i)
    {
        memset(final,0,sizeof(final));
        if(DFS(i)) maxmatch++;
    }
    return maxmatch;
}
int main()
{
    //freopen("1.txt","r",stdin);
    int i,j,k;
    bool flag;
    int v;
    int strsize;
    while(scanf("%d%d",&n,&m)!=-1)
    {                
        if(n==0 && m==0)
            break;
        numcount = 0;
        memset(map,0,sizeof(map));
        memset(match,0,sizeof(match));
        memset(refinal,0,sizeof(refinal));
        for(i=1;i<=m;++i)
        {
             flag = false;
             scanf("%s",str);
             strsize = strlen(str);       //使用strlen,此处不要使用sizeof
             for(j=0;j<strsize;++j)
             {
                 if(str[j]=='*')
                 {
                     flag = true;
                     v = j;
                     break;
                 }
             }
             if(flag)
             {
                 str[v]='0';
                 refinal[strtoint(str)] = true;
                 str[v]='1';
                 refinal[strtoint(str)] = true;                                
             }
             else
             {
                 refinal[strtoint(str)] = true;
             }                        
        }
        for(i=0;i<=marray;++i)
        {
             if(refinal[i])
             {
                 numcount++;
                 num[numcount] = i;
             }
        }       
        for(i=1;i<=numcount;++i)
        {
             for(j=1;j<=numcount;++j)
             {        
                 if(isConnect(num[i]^num[j]))
                    map[i][j]=1;
             }
        }
        cout<<(numcount-mat()/2)<<endl;  
    }
    //getch();
    return 0;
}

转载于:https://www.cnblogs.com/north_dragon/archive/2010/06/01/1749502.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值