poj 2724 二部图最大匹配建图(干净的奶酪)

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

思路:建图想了好久,对问题的敏感性还是不够。实际上两部的选择比较显然,就是按照串中1的数量是奇还是偶来分。代码写的丑的一逼,唉。

#include <stdio.h>
#include <string.h>
#define N 2050
int n,m,k,a;
char s[12];
int p[N],q[N],used[N],link[N],g[N][N];
int dfs(int i){
	int j;
	for(j = 1;j<=m;j++)
		if(!used[j] && g[i][j]){
			used[j] = 1;
			if(link[j] == -1 || dfs(link[j])){
				link[j] = i;
				return 1;
			}
		}
	return 0;
}
int hungary(){
	int i,res=0;
	for(i = 1;i<=n;i++){
		memset(used,0,sizeof(used));
		if(dfs(i))
			res++;
	}
	return res;
}
int hasstar(){//判断串中是否有*
	int i;
	for(i = 0;i<a;i++)
		if(s[i] == '*')
			return i;
	return -1;
}
int isodd(){//判断串中1的个数是否为奇数个
	int i,num=0;
	for(i = 0;i<a;i++)
		if(s[i] == '1')
			num++;
	return num&1;
}
int test(int x,int y){//测试两个串是否只是一位不同
	int i,num=0;
	x ^= y;
	for(i = 0;i<a;i++)
		if((1<<i) & x)
			num++;
	return num==1;
}
void addedge(int flag){//向二部图中添加边
	int num=0,i;
	for(i = 0;i<a;i++){
		num += (s[i]-'0');
		num <<= 1;
	}
	num >>= 1;
	if(!flag){
		for(i = 1;i<=n;i++)//看看这个点是否之前已经有了
			if(num == p[i])
				return;
		p[++n] = num;
		for(i = 1;i<=m;i++)
			if(test(num , q[i]))
				g[n][i] = 1;
	}else{
		for(i = 1;i<=m;i++)
			if(num == q[i])
				return;
		q[++m] = num;
		for(i = 1;i<=n;i++)
			if(test(num,p[i]))
				g[i][m] = 1;
	}
}
void update(){
	int j;
	j = hasstar();
	if(j!=-1){
		if(isodd()){//有*的话,分成为1和为0两种分别讨论
			s[j] = '0';
			addedge(0);
			s[j] = '1';
			addedge(1);
		}else{
			s[j] = '1';
			addedge(0);
			s[j] = '0';
			addedge(1);
		}
	}else{
		if(isodd())
			addedge(0);
		else
			addedge(1);
	}
}
int main(){
	freopen("a.txt","r",stdin);
	while(scanf("%d %d\n",&a,&k) && (a+k)){
		int i;
		memset(g,0,sizeof(g));
		memset(link,-1,sizeof(link));
		n = m = 0;
		for(i = 0;i<k;i++){
			scanf("%s",s);
			update();
		}
		printf("%d\n",n+m-hungary());
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值