网络流二十四题——软件补丁问题

Luogu P2761 软件补丁问题 一道超蒻的蓝题

软件补丁问题

题目描述

T 公司发现其研制的一个软件中有 \(n\) 个错误,随即为该软件发放了 \(m\) 个补丁程序。
每一个补丁程序都有其特定的适用环境,某个补丁只有在软件中包含某些错误而同时又不包含另一些错误时才可以使用。一个补丁在排除某些错误的同时,往往会加入另一些错误。
换句话说,对于任意一个补丁 \(i\),都有四个与之相应的集合 \(B1_i,B2_i,F1_i\)\(F2_i\)。仅当软件包含 \(B1_i\) 中的所有错误,而不包含 \(B2_i\) 中的任何错误时,才可以使用补丁 \(i\)。补丁 \(i\) 将修复软件中的某些错误集合 \(F1_i\),而同时加入另一些错误 \(F2_i\)。另外,运行每个补丁都耗费一定的时间。
试设计一个算法,利用 T 公司提供的 \(m\) 个补丁程序将原软件修复成一个没有错误的软件,并使修复后的软件耗时最少。对于给定的 \(n\) 个错误和 \(m\) 个补丁程序,找到总耗时最少的软件修复方案。

输入格式

第一行有两个正整数 \(n\)\(m\)\(n\) 表示错误总数,\(m\)表示补丁总数。
接下来 \(m\) 行给出了 \(m\) 个补丁的信息。每行包括一个正整数,表示运行补丁程序 \(i\) 所需时间,以及两个长度为 \(n\) 的字符串。中间用一个空格符隔开。
第一个字符串中,如果第 \(k\) 个字符为 +,则表示第 \(k\) 个错误属于 \(B1_i\)。若为 -,则表示第 \(k\) 个错误属于 \(B2_i\)。若为 0,则第 \(k\) 个错误既不属于 \(B1_i\) 也不属于 \(B2_i\),即软件中是否包含第 \(k\) 个错误并不影响补丁 \(i\) 的可用性。
第二个字符串中,如果第 \(k\) 个字符为 -,则表示第 \(k\) 个错误属于 \(F1_i\)。若为 +,则表示第 \(k\) 个错误属于 \(F2_i\)。若为 0,则第 \(k\) 个错误既不属于 \(F1_i\) 也不属于 \(F2_i\),即软件中是否包含第 \(k\) 个错误不会因使用补丁 \(i\) 而改变。

输出格式

程序运行结束时,将总耗时数输出。如果问题无解,则输出 0

数据范围

对于 \(100\%\) 的数据:\(1\le n\le 20\)\(1\le m\le 100\)

题目大意

给定一个具有\(n\)个特征的状态,和两个字符串\(s1\)\(s2\)来表示\(B1_i,B2_i,F1_i,F2_i\)的信息,当且仅当当前的状态具有\(B1_i\)中的所有状态,不含有\(B2_i\)的任何状态时候;能够在t的代价下将\(n\)中的\(F1_i\)去掉,引入\(F2_i\),求从初始状态全为1到最终状态全为0所需要的最小代价

分析

涉及到状态很容易就想到状态压缩,我们定义当前状态\(sta\)中二进制位下\(1\)表示含有当前错误,\(Vice Versa\).

处理1

对于给出的字符串\(s1\),我们将他压为一个二进制数,\(B_1\)\(1\)(+)表示要含有当前错误,\(B_2\)\(0\)(-)表示不能有当前错误
那么显而易见,当前状态能使用此补丁的充要条件为:
\(sta\)&\(B_1==B1\)并且\(sta\)&\(B_2==0\)

处理2

对于给出的字符串\(s2\),同样做二进制化操作,\(f1\)中的\(1\)代表会消除\(sta\)在这个位置上的\(1\)\(f2\)中的\(1\)表示会带来\(sta\)在这个位置上的\(1\),那么经过了我们处理过后,状态\(sta\)会变成:
首先带来\(f2\)的错误,\(sta|=f2\)
其次消除\(f1\)的错误,\(sta|=f1\rightarrow\)\(sta\)^\(=f1\)
(因为f1中所有的错误都会被消除,所以大可直接假设\(sta\)中含有所有f1的错误再消除)

处理3

既然要求最小的花费,又涉及到状态与状态之间转移的代价,那么很容易就想到用最短路,这里我用的是\(SPFA\)\(Dijkstra\)应该也是没问题的,但是没有尝试过。先把处理好的初始状态\(sta_0\)入队,再按照正常的\(SPFA\)来做就行了,这里提一嘴个人很容易打错的点,就是只有转移的状态既能够带来更优的结果并且当前状态没有在更新队列里面的时候我们才将它入。这也是为什么q.push(v),vis[v]=1;放在了松弛条件if(dis[x]+p[i].w<dis[v])的里面

点击查看代码
void spfa()
{
	memset(dis,0x7f,sizeof dis);
	dis[start]=0;q.push(start);vis[start]=1;
	while(!q.empty())
	{
		int x=q.front();
		for(int i=1;i<=m;i++)
		{
			if(usable(x,i))
			{
				int v=trans(x,i);
				if(dis[x]+p[i].w<dis[v])
				{
					dis[v]=dis[x]+p[i].w;
					if(!vis[v])	q.push(v),vis[v]=1;;
				}
			}
		}
		vis[x]=0,q.pop();
	}
}

Code

点击查看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define maxn 100000
using namespace std;
struct fix{int b1,b2,f1,f2,w;}p[maxn];
int n,m;char c[maxn];int start,end=0;
bool usable(int x,int i){return ((x&p[i].b1)==p[i].b1)&&(!(x&p[i].b2));}
int trans(int x,int i){return ((x|p[i].f2)|p[i].f1)^p[i].f1;}
void pre()
{
	scanf("%d%d",&n,&m);
	start=(1<<n)-1;
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&p[i].w);
		scanf("%s",c);int len=strlen(c);
		for(int j=0;j<len;j++)
		{
			p[i].b1<<=1,p[i].b2<<=1;
			if(c[j]=='+')p[i].b1+=1;
			else if(c[j]=='-')p[i].b2+=1;
		}
		scanf("%s",c);len=strlen(c);
		for(int j=0;j<len;j++)
		{
			p[i].f1<<=1,p[i].f2<<=1;
			if(c[j]=='-')p[i].f1+=1;
			else if(c[j]=='+')p[i].f2+=1;
		}
	}
}

int dis[1<<22],vis[1<<22];queue<int>q;
void spfa()
{
	memset(dis,0x7f,sizeof dis);
	dis[start]=0;q.push(start);vis[start]=1;
	while(!q.empty())
	{
		int x=q.front();
		for(int i=1;i<=m;i++)
		{
			if(usable(x,i))
			{
				int v=trans(x,i);
				if(dis[x]+p[i].w<dis[v])
				{
					dis[v]=dis[x]+p[i].w;
					if(!vis[v])	q.push(v),vis[v]=1;;
				}
			}
		}
		vis[x]=0,q.pop();
	}
}

int main()
{
	pre();
	spfa();
	if(dis[0]==dis[1<<22-1])
		printf("0");
	else
		printf("%d",dis[0]);
	return 0;
}

It has come to an END

posted @ 2022-09-14 22:27  Hanggoash  阅读(31)  评论(0编辑  收藏  举报
动态线条
动态线条end