Purifying Machine POJ - 2724

Purifying Machine POJ - 2724

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 01100, 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

Sponsor

解答

1.给出一系列的操作,其中’*‘最多1个,既可表示为’0’,也可表示为’1’,该操作对应的那个chess被感染


101 //编号101(2进制,下同)的被感染

1*1
//编号101,111的被感染
2.提问,最少需要多少步,才可以把所有感染的chess全部变好,并且要求操作不能影响到其他任何的没感染的chess.

3 3
*01
100
011

被感染的ID是:
101
001
100
011
所需要的操作:
10*
0*1
所以答案是2

由题目的意思很明显得知操作步数<=M.于是想到了匹配,如果输入中存在2个数字,他们的2进制表示只相差1位,那么就可以使用一个包含’*'的操作来实现,也可以很容易的知道一个操作最多只能操作2个ID。现在对于输入的数字首先要

  1. 判重
  2. 建边

建边的时候有一个小优化,就是求A,B的2进制表示是否只差1位

C=A^B;
C&&((C&(C-1))==0)的结果就表示他们是否只差1位

C&(C-1)表示去掉C最后一个1,如果去掉之后为0表示不相同的只有1位,而且C必须不为0。所以两个条件都满足就可以表示A,B二进制位只差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

…etc…

二进制只有一位不同的数可建边,边表示带*的操作。由于建边的两个数二进制中1的个数必为一奇一偶,所以图为二分图。匈牙利算法求得最大二分匹配,被污染的奶酪数减去最大匹配即为答案。由于匹配是无向图,所以还要除以2。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
const int N = 1110;
bool mark[N];
int mat[N];
vector<int> g[N];
int n, m;
bool flag[N];
int map[N];
bool find(int x)
{
    int i, tem;
    for (i = 0; i < g[x].size(); i++) {
        tem = g[x][i];
        if (!mark[tem]) {
            mark[tem] = true;
            if (mat[tem] == -1 || find(mat[tem])) {
                mat[tem] = x;
                return true;
            }
        }
    }
    return false;
}
int hungary()
{
    int i, ans = 0;
    memset(mat, -1, sizeof(mat));
    for (i = 0; i < n; i++) {
        memset(mark, false, sizeof(mark));
        if (find(i))
            ans++;
    }
    return ans;
}
bool judge(int x, int y)
{
    int z = x ^ y;
    if (z && (z & (z - 1)) == 0)
        return true;
    else
        return false;
}
int getnum(char* str)
{
    int i, s = 0;
    int len = strlen(str);
    for (i = len - 1; i >= 0; i--) {
        s = s * 2 + str[i] - '0';
    }
    return s;
}
int main()
{
    int i, j, k;
    char ch[15];
    while (cin >> n >> m) {//n台机器
        if (n == 0 && m == 0)
            break;
        for (i = 0; i < 1024; i++)
            g[i].clear();
        memset(flag, false, sizeof(flag));
        for (i = 0; i < m; i++) {//m个操作序列
            scanf("%s", ch);
            for (j = 0; ch[j] != '\0'; j++)
                if (ch[j] == '*')
                    break;//有这个j了跳出内层循环
            if (ch[j]) {//两种情况都加上
                ch[j] = '0';
                flag[getnum(ch)] = true;
                ch[j] = '1';
                flag[getnum(ch)] = true;
            } else//注意'\0'ascii码是0,所以表示一直到末尾都没有发现星号
                flag[getnum(ch)] = true;
        }
        int size = 1 << n;
        k = 0;
        for (i = 0; i < size; i++) {
            if (flag[i])
                map[k++] = i;//map记录总的数字序列
        }
        for (i = 0; i < k; i++) {
            for (j = i + 1; j < k; j++) {
                if (judge(map[i], map[j])) {//只有一位不同可以建边。
                    g[i].push_back(j);
                    g[j].push_back(i);
                }
            }
        }
        n = m = k;
        cout << n - hungary() / 2 << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值