「网络流 24 题」软件补丁

题目描述

某公司发现其研制的一个软件中有 n nn 个错误,随即为该软件发放了一批共 m mm 个补丁程序。每一个补丁程序都有其特定的适用环境,某个补丁只有在软件中包含某些错误而同时又不包含另一些错误时才可以使用。一个补丁在排除某些错误的同时,往往会加入另一些错误。

换句话说,对于每一个补丁 i ii,都有 2 22 个与之相应的错误集合 B1(i) B_1(i)B1(i) 和 B2(i) B_2(i)B2(i),使得仅当软件包含 B1(i) B_1(i)B1(i) 中的所有错误,而不包含 B2(i) B_2(i)B2(i) 中的任何错误时,才可以使用补丁 i ii。补丁 i ii将修复软件中的某些错误 F1(i) F_1(i)F1(i),而同时加入另一些错误 F2(i) F_2(i)F2(i)。另外,每个补丁都耗费一定的时间。

试设计一个算法,利用公司提供的 m mm 个补丁程序将原软件修复成一个没有错误的软件,并使修复后的软件耗时最少。

输入格式

文件第 1 11 行有 2 22 个正整数 n nn 和 m mmn nn 表示错误总数,m mm 表示补丁总数。接下来 m mm 行给出了 m mm 个补丁的信息。每行包括一个正整数,表示运行补丁程序 i ii 所需时间,以及 2 22 个长度为 n nn 的字符串,中间用一个空格符隔开。

第 1 11 个字符串中,如果第 k kk 个字符 bk b_kbk 为 +,则表示第 k kk 个错误属于 B1(i) B_1(i)B1(i)。若为 -,则表示第 k kk 个错误属于 B2(i) B_2(i)B2(i),若为 0,则第 k kk 个错误既不属于 B1(i) B_1(i)B1(i) 也不属于 B2(i) B_2(i)B2(i),即软件中是否包含第 k kk 个错误并不影响补丁 i ii 的可用性。
第 2 22 个字符串中,如果第 k kk 个字符 bk b_kbk 为 +,则表示第 k kk 个错误属于 F1(i) F_1(i)F1(i),若为 -,则表示第 k kk 个错误属于 F2(i) F_2(i)F2(i),若为 0,则第 k kk 个错误既不属于 F1(i) F_1(i)F1(i) 也不属于 F2(i) F_2(i)F2(i),即软件中是否包含第 k kk 个错误不会因使用补丁 i ii 而改变。

输出格式

输出最小耗时,如果问题无解,则输出 0 00

样例
样例输入
3 3
1 000 00-
1 00- 0-+
2 0-- -++
样例输出
8


这题可以直接spfa。每种状态安位转化,那么(1<<n)-1为有n个错误的初始状态,需要到达为错误的0状态,
对于每个补丁的要求和产生的效果可以用不同的状态表示,所以可用用spfa状态转移求最小花费。
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
const int maxm = 105;
const int INF = 1e9 + 7;
int b1[maxm], b2[maxm], f1[maxm], f2[maxm], dis[1200005], cost[maxm];
int s, t, n, m;
char str[maxm];
void spfa();
int main()
{
	int i, j, k, sum;
	scanf("%d%d", &n, &m);
	for (i = 1;i <= m;i++)
	{
		scanf("%d", &cost[i]);
		scanf("%s", str);
		for (j = 0;j < n;j++)
		{
			if (str[j] == '+') b1[i] += 1 << j;
			else if (str[j] == '-') b2[i] += 1 << j;
		}
		scanf("%s", str);
		for (j = 0;j < n;j++)
		{
			if (str[j] == '-') f1[i] += 1 << j;
			else if (str[j] == '+') f2[i] += 1 << j;
		}
	}
	s = (1 << n) - 1, t = 0;
	spfa(); 
	if (dis[t] == INF) printf("0\n");
	else printf("%d\n", dis[t]);
	return 0;
}
void spfa()
{
	queue<int>q;
	for (int i = 0;i <= s;i++) dis[i] = INF;
	dis[s] = 0;
	q.push(s);
	while (!q.empty())
	{
		int u = q.front();q.pop();
		for (int i = 1;i <= m;i++)
		{
			if ((u&b1[i]) == b1[i] && (u&b2[i]) == 0)
			{
				int v = u - (u&f1[i]);v |= f2[i];
				if (dis[v] > dis[u] + cost[i])
				{
					dis[v] = dis[u] + cost[i];
					q.push(v);
				}
			}
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值