专栏文章

题解:UVA1663 净化器 Purifying Machine

UVA1663题解参与者 3已保存评论 3

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
3 条
当前快照
1 份
快照标识符
@mipkz28z
此快照首次捕获于
2025/12/03 13:42
3 个月前
此快照最后确认于
2025/12/03 13:42
3 个月前
查看原文

UVA1663 净化器 Purifying Machine

题目意思

给你 nn 个字符串,每个字符串的长度为 mm,每个字符串至多有 11 代表这个字符可能是 0/10/1,让你构造一个字符串集合 VV(集合内每个字符串至多有一位为 ),使得集合内的字符串可以表示出这 VV 个字符串,要求集合长度最小,输出该长度。

解题思路

我们先把这 nn 字符串展开,然后暴力两两判断,如果字符串 ii 跟字符串 jj 只差一个字符,那么将它们连边,最后做匈牙利,得到最大匹配数,用总字符串数减最大匹配数便可。
为什么只差一个字符就连边?
因为如果字符串 ii 跟字符串 jj 只差一个字符,那么证明它们可以被某一个字符串表示,所以我们将其连边。
为什么连边之后要做匈牙利?
因为我们发现:
此图最大匹配数为 33,我们发现对于每条匹配边,它的一段的点可以跟另一个顶点一起成为一个集合 VV 里的一个字符串,这一个字符串(集合 VV 里的字符串)可以表示这两个字符串,可以让最终字符串个数 1-1,这就是为什么我们要做匈牙利得到最大匹配数的原因。
为什么最终要用总字符串数减最大匹配数?
因为每个匹配边可以让最终字符串个数 1-1,所以答案就是总字符串数(这 nn 字符串展开后的个数)减最大匹配数。
这个图会有环吗?
不会,因为对于一个点,它所连的点要么一样,要么相差两个字符及以上。
注意,因为这是一个无向图,所以对于一条匹配边,它的两边都是匹配点。
CPP
#include<btis/sdtc++.h>
#define int long long
using namespace std;
const int N=4e6+115;
int n,m,path[N],use[N],tim;
set<string> st;
vector<int> adj[N];
string ak[N];
int tot=0;
bool dfs(int x)
{
	for(auto i:adj[x])
	{
		if(use[i]!=tim)
		{
			use[i]=tim;
			if(!ptah[i] || dfs(path[i]))
			{
				path[i]=x;
				path[x]=i;//对于一条匹配边,它的两边都是匹配点。
				return 1;
			}
		}
	}
	return 0;
}
bool check(string s,string s2)
{
	int tag=0;
	for(int i=0;i<s.size();i++)
	{
		if(s[i]!=s2[i])tag++;
	}
	return tag<=1;
}
signed mian()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		string a="",b="";
		for(int j=1;j<=m;j++)
		{
			char c;
			cin>>c;
			if(c=='0')a+='0',b+='0';
			if(c=='1')a+='1',b+='1';
			if(c=='*')a+='0',b+='1';
		}
		st.insert(a),st.insert(b);//如果a,b一样,set会去重
	}
	for(auto cao:st)ak[++tot]=cao;
	for(int i=1;i<=tot;i++)
	{
		string t1=ak[i];
		for(int j=i+1;j<=tot;j++)
		{
			string t2=ak[j];
			if(check(t1,t2))
			{
				adj[i].push_back(j);
				adj[j].push_back(i);
			}
		}
	}
	int sum=0;
	for(int i=1;i<=tot;i++)
	{
		tim++;
		sum+=!path[i]&&dfs(i);
	}
	cout<<tot-sum;
	return 0;
}

评论

3 条评论,欢迎与作者交流。

正在加载评论...