NKOI 1947 软件补丁

19 篇文章 0 订阅
19 篇文章 0 订阅

【线性规划与网络流24题 12】软件补丁

Time Limit:10000MS  Memory Limit:65536K
Total Submit:15 Accepted:1 
Case Time Limit:1000MS

Description

T 公司发现其研制的一个软件中有n 个错误,随即为该软件发放了一批共m 个补丁程序。每一个补丁程序都有其特定的适用环境,某个补丁只有在软件中包含某些错误而同时又不包含另一些错误时才可以使用。一个补丁在排除某些错误的同时,往往会加入另一些错误。 
换句话说,对于每一个补丁i,都有2 个与之相应的错误集合B1[i]和B2[i],使得仅当软件包含B1[i]中的所有错误,而不包含B2[i]中的任何错误时,才可以使用补丁i。补丁i 将修复软件中的某些错误F1[i],而同时加入另一些错误F2[i]。另外,每个补丁都耗费一定的时间。试设计一个算法,利用T 公司提供的m个补丁程序将原软件修复成一个没有错误的软件,并使修复后的软件耗时最少。 

编程任务: 
对于给定的n个错误和m个补丁程序,找到总耗时最少的软件修复方案。

Input

第1 行有2 个正整数n 和m,n 表示错误总数,m表示补丁总数,1<=n<=20, 1<=m<=100。 
接下来m行给出了m个补丁的信息。每行包括一个正整数,表示运行补丁程序i 所需时间,以及2 个长度为n的字符串,中间用一个空格符 
隔开。第1 个字符串中,如果第k个字符bk为“+”,则表示第k个错误属于B1[i],若为“-”,则表示第k个错误属于B21[i],若为“0”,则第k个错误既不属于B1[i]也不属于B2[i],即软件中是否包含第k个错误并不影响补丁i的可用性。第2 个字符串中,如果第k个字符bk为“+”,则表示第k个错误属于F1[i],若为“-”,则表示第k个错误属于F2[i],若为“0”,则第k个错误既不属于F1[i]也不属于F2[i],即软件中是否包含第k个错误不会因使用补丁i 而改变。

Output

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

Sample Input

3 3
1 000 00-
1 00- 0-+
2 0-- -++

Sample Output

8

Source

感谢 Wo_ai_WangYuan 放上数据


说实话真不知道这题和网络流有什么关系,第一个想到的解法是状压+dfs求最短路

对于一个状态s,如果第i个错误存在,那么s&(1<<i-1)==1,因此我们很容易就能发现:

初始点target,终点0,从s到状态t的代价为能使s到达t的补丁花费,那么我们求一个最短路就可以了

只要最后dis[0]<inf就可以直接输出了,否则为0

#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=1<<21,inf=1e9,maxm=105;
inline void _read(int &x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9')
    {if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
int n,m,target,c[maxm],s1[maxm],s2[maxm],s3[maxm],s4[maxm];
int dis[maxn];
string ss,tt;
void dfs(int k){
	if(dis[k]>=dis[0])return;
	for(int i=1;i<=m;i++)
	    if((k&s2[i])==s1[i]){//这里和下面一排的位运算很机智,仔细揣摩
	    	int temp=k&s4[i]|s3[i];
	    	if(dis[k]+c[i]<dis[temp]){
	    		dis[temp]=dis[k]+c[i];
	    		dfs(temp);
			}
		}
}
int main(){
	_read(n);_read(m);
	target=(1<<n)-1;
	int i,j;
	for(i=0;i<target;i++)dis[i]=inf;
	for(i=1;i<=m;i++){
		_read(c[i]);
		cin>>ss>>tt;
		for(j=0;j<n;j++){
			if(ss[j]=='+')s1[i]|=1<<j;
			if(ss[j]!='0')s2[i]|=1<<j;
		}
		for(j=0;j<n;j++){
			if(tt[j]=='+')s3[i]|=1<<j;
			if(tt[j]!='-')s4[i]|=1<<j;
		}
	}
	dfs(target);
	printf("%d",dis[0]>=inf?0:dis[0]);
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值