洛谷P2761:软件补丁问题

题目大意

给出n个错误与m种不同的补丁,每种补丁当且仅当b1[i]包含当前状态的的错误,且b2[i]不包含当前状态下的

错误(b1[i]与b2[1]是两个集合,题目给出)。

每个补丁都有他自己的运行时间,i补丁会修复在f1[i]里面的错误,同时也会带来f2[i]里面的错误。(因为这个,

题目从简单的二分图最大匹配问题变得很复杂。至少在你没有看出来是最短路求解之前)

解法

      很多人都说这题不应该出在网络流24题里面,因为这题用网络流做会很复杂(因为我不懂),所以大多数A了这题的人跟我一样,都是用最短路+状态压缩求解,其实也很好解释。

      用queue[i]来作为最短路的队列,里面存的是一个状态,因为总错误只有20个,所以对于第k个错误来说,如果已经被处理过了,那么倒数第k位就是0;否则就是1,相当于加上1<<(k-1);

      很多人会说,你讲那么快,我怎么会看的懂?妖怪吧。。

     那我们来举一个例子比如说现在一共有5个错误,1错误被处理掉了,3错误也被处理掉了,那么状态就是11010(26)。没错,n<=20,所以这个东西转成10进制之后不过才100多万。你可以像我一样开一个循环队列或者是系统的队列都行,希望大家能听懂咯。。

      每一次迭代一遍补丁,找到满足条件的补丁后,算出目标状态,判断当前状态所用时间+补丁所用时间<原来花费时间,那么就把原来时间替换掉(=当前状态所用时间+补丁所用时间),加进队列,继续操作,最后判断一下0(及所有都被处理掉时)状态是否被查找过,如果是,那么输出0状态时间,否则就输出0。完了。希望大家喜欢,下面的是c++代码。

#include<cstdio>
#include<cstdlib>
#include<cstring>

int time[110];
int n,m;
int b1[110],b2[110],f1[110],f2[110];
int queue[1050010];
int d[1050010];
int st,ed;

void bfs()
{
	memset(d,63,sizeof(d));
	st=1,ed=2;
	queue[st]=(1<<n)-1;
	d[(1<<n)-1]=0;
	while(st!=ed)
	{
		for(int i=1;i<=m;i++)
		{
			if((queue[st] & b1[i]) == b1[i] && (queue[st] & b2[i]) == 0)
			{
				int op=(queue[st] & (~f1[i]));
				op=(op | f2[i]);
				if(d[queue[st]]+time[i]<d[op])
				{
					d[op]=d[queue[st]]+time[i];
					queue[ed]=op;
					ed++;
					if(ed>105000) ed=1;
				}
			}
		}
		st++;
		if(st>105000) st=1;
	}
}

int main()
{
	char s[25];
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&time[i]);
		scanf("%s",s+1);
		for(int j=1;j<=n;j++)
		{
			if(s[j]=='+') b1[i]+=(1<<(j-1));
			if(s[j]=='-') b2[i]+=(1<<(j-1));
		}
		scanf("%s",s+1);
		for(int j=1;j<=n;j++)
		{
			if(s[j]=='-') f1[i]+=(1<<(j-1));
			if(s[j]=='+') f2[i]+=(1<<(j-1));
		}
	}
	bfs();
	if(d[0]==1061109567)
		printf("0\n");
	else 
		printf("%d",d[0]);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值