[精]poj2724

Purifying Machine
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 5027   Accepted: 1455

Description

Mike is the owner of a cheese factory. He has 2N cheeses and each cheese is given a binary number from 00...0 to 11...1. To keep his cheese free from viruses, he made himself a purifying machine to clean virus-infected cheese. As a talented programmer, his purifying machine is built in a special way. His purifying machine has N switches, each switch has three states, 1, 0 and *. An operation of this machine is a cleaning action according to the states of the N switches. During one operation, at most one switch can be turned to state *, which can substitute for either 1 or 0. When the machine is turned to a specific state, an operation will clean all the cheeses with corresponding binary numbers. For example, if N equals 6 and the switches are turned to 01*100, the cheeses numbered 010100 and 011100 are under operation by the machine.

One day, Mike's machine was infected. When Mike found out, he had already done some operations and the cheeses operated by this infected machine were infected too. He cleaned his machine as quickly as he could, and now he needs to clean the infected cheeses with the minimum number of operations. If a cheese is infected, cleaning this cheese with the machine one or more times will make this cheese free from virus again; but if a cheese is not infected, operation on this cheese will make it go bad.

Now given the infected operations Mike has done, you need to find out the minimum number of operations that must be performed to clean all the infected cheeses without making any clean cheese go bad.

Input

There are several test cases. Each test case starts with a line containing two numbers N and M (1 <= N <= 10, 1 <= M <= 1000). N is the number of switches in the machine and M is the number of infected operations Mike has done. Each of the following M lines contains a switch state of the machine. A test case with N = M = 0 ends the input and should not be processed.

Output

For each test case, output one line containing an integer, which is the minimum number of operations Mike needs to do.

Sample Input

3 3
*01
100
011
0 0

Sample Output

2

Source

题意:

题意:迈克有一台可以净化奶酪的机器,用二进制表示净化的奶酪的编号。但是,在某些二进制串中可能包含有‘*'。例如01*100,'*'其实就代表可以取0,1两种情况--> 010100 和011100。现在由于迈克不小心,他以同样的方式弄脏了某些奶酪,问你最少用多少次操作就可以把弄脏的奶酪全净化好。(没有被弄脏过的奶酪不能净化。弄脏过的奶酪可以多次净化。)

思路:

也就是给你一些不同的(判重之后)二进制串,每个串可以通过1次操作净化,也可以把两个只有1位不同的串通过1次操作联合净化.要我们求最少的操作次数.

       我们把所有串按其中1的个数和是奇还是偶分成左右两个点集.

对于任意两个串,如果他们只有1位不同,那么就在他们之间连接一条无向边.(这两个串一定分别属于不同的点集)

       由于串的总数是固定的,且一个串可以通过单独净化也可以通过联合净化.而我们向让净化的次数最少,我们自然想联合净化(即一次可以净化两个串)的次数尽量多了. 那么我们最多可以进行多少次联合净化呢? 这个数值==我们建立二分图的最大匹配边数.(想想是不是,因为一个串最多只能被净化一次)

      假设总的不同串有n个,我们建立二分图的最大匹配数(即联合净化最大次数)为ans,那么我们总共需要n-ans次净化即可.(想想为什么)

       当然本题也可以不用把串特意分成左右点集(本程序实现就是用的这种方式:未分左右点集),我们只需要把原图翻倍,然后求翻倍图的最大匹配数ans,最后用n-ans/2即可.

这个题中有很多位运算的有意思的东西

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3000+50;
int uN,vN;
int g[N][N];
int link[N];
int used[N];
int num[N];
int dfs(int u){
    for(int v=0;v<vN;v++)//顶点编号从0开始的 
        if(g[u][v]&&!used[v]){
            used[v]=1;
            if(link[v]==-1||dfs(link[v])){
                link[v]=u;
                return 1;
            }
        }
    return 0;
}
int hungary(){
    int res=0;
    memset(link,-1,sizeof(link));
    for(int u=0;u<uN;u++){
        memset(used,0,sizeof(used));
        if(dfs(u)) res++;
    }
    return res;
}
int main(){
    int n,m;
    char str[50];
    while(scanf("%d%d",&n,&m)==2&&(m+n)){
        memset(g,0,sizeof(g));
        memset(num,0,sizeof(num));
        int cnt=0;
        while(m--){
            cnt++;
            scanf("%s",&str);
            int pos=-1;
            for(int i=0;i<n;i++){
                if(str[i]=='*'){pos=i;continue;}
                num[cnt]|=(str[i]-'0')<<i;
            }
            if(pos!=-1){
                cnt++;
                num[cnt]=(num[cnt-1]|(1<<pos));
            }
        }
        sort(num+1,num+cnt+1);
        num[0]=-1;
        int i,j;
        for(j=0,i=1;i<=cnt;i++){
            if(num[j]!=num[i])
                num[++j] = num[i];
        }
        cnt=j;
        for(i=1;i<=cnt;i++)
            for(j=1;j<=cnt;j++){
                int c=num[i]^num[j];
                if(c&&((c&(c-1))==0))g[num[i]][num[j]]=1;
            }
        uN=vN=2050;
        int ans=cnt-hungary()/2;
        printf("%d
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/shenben/p/5634144.html