Purifying Machine(UVA-1663)

这篇博客讲解了如何使用二分图和二进制技巧来解决给定多个长度为n的0/1字符串,其中部分位置可能被星号替代的问题。通过构建二分图并利用匈牙利算法找到最大匹配,最终确定最少需要多少个修改后的字符串来表示所有原始串,同时提及了位运算技巧的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

题意
给定m 个长度为n 的0/1串,其中每个串至多有一位被挖掉,替换成了∗ ,表示这个位置可能是0/1。问最坏情况下最少要用多少个0/1串(也可以至多有一位为∗ )能表示这m个串
思路
一个模板可能匹配一个串或两个串,要让能匹配两个串的模板尽可能的多,建立二分图,能用一个模板匹配的串连边。答案即为实际的串数减去最大匹配数的一半(由于我们未划分二分图,所以每一组匹配会被计算两次,需要除以二)

tips:位运算小技巧:x第y位置0: x|=(1<<y);x第y位置1:x&=~(1<<y)。
代码如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e3+5;
const int inf=0x3f3f3f3f;
typedef long long ll;
int n,m;
int t[maxn];
int vis[maxn];
int G[maxn][maxn];
int match[maxn];
int f[maxn];
inline int find(int x)//匈牙利算法
{
    for(int i=0;i<(1<<n);i++)
    {
        if(G[x][i]&&!vis[i])
        {
            vis[i]=1;
            if(!match[i]||find(match[i]))
            {
                match[i]=x;
                return 1;
            }
        }
    }
    return 0;
}


int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin>>n>>m&&n&&m)
    {
        string s;
        memset(G,0,sizeof(G));
        memset(t,0,sizeof(t));
        for(int i=0;i<m;i++)
        {
            cin>>s;
            int x=0,pos=-1;
            for(int j=0;j<n;j++)
            {
                if(s[j]=='1')
                {
                    x+=1<<j;
                }
                else if(s[j]=='*')
                {
                    pos=j;
                }
            }
            t[x]=1;//标记有这个数
            if(pos!=-1)//如果有*,则把另外一种情况也算上
                t[x^(1<<pos)]=1;
        }
        int cnt=0;//cnt表示实际的串数
        for(int i=0;i<(1<<n);i++)
        {
            if(t[i])
            {
                cnt++;
                for(int j=0;j<n;j++)
                {
                    if(t[i^(1<<j)])
                    {
                         G[i][i^(1<<j)]=1;//与和它只有一位不同的串连边
                    }

                }

            }
        }
        int ans=0;//最大有几个匹配数
        memset(match,0,sizeof(match));
        for(int i=0;i<(1<<n);i++)//匈牙利算法
        {
            memset(vis,0,sizeof(vis));
            ans+=find(i);
        }
        cout<<cnt-ans/2<<endl;
    }
}

二分图+二进制的小技巧
我人傻了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值