zoukankan      html  css  js  c++  java
  • Libre 6009 「网络流 24 题」软件补丁 / Luogu 2761 软件安装问题 (最短路径,位运算)

    Libre 6009 「网络流 24 题」软件补丁 / Luogu 2761 软件安装问题 (最短路径,位运算)

    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

    Http

    Libre:https://loj.ac/problem/6009
    Luogu:https://www.luogu.org/problem/show?pid=2761

    Source

    最短路径,位运算

    解决思路

    话说这不是一道最短路径的题目吗?为什么放在了网络流里面?不懂╮(╯▽╰)╭
    我们用位运算的方式表示一个错误有没有修复。对于错误i,若第i-1位是1则表示该错误存在,否则表示不存在。而每一次转移就根据位运算的操作来判断是否满足某些错误存在而另一些错误不存在的情况。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int maxN=300;
    const int maxM=maxN*maxN*2;
    const int inf=2147483647;
    
    int n,m;
    unsigned int B1[maxN];//B1,B2,F1,F2的意义与题目一样
    unsigned int B2[maxN];
    unsigned int F1[maxN];
    unsigned int F2[maxN];
    int Cost[maxN];
    int Q[(1<<21)];
    int Dist[(1<<21)];
    bool inqueue[(1<<21)];
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=m;i++)
    	{
    		char str[maxN];
    		scanf("%d%s",&Cost[i],str);
    		for (int j=0;j<n;j++)
    			if (str[j]=='+')
    				B1[i]=B1[i]|(1<<(j));//这里是标记某个错误是否出现,注意标号从0开始
    			else
    				if (str[j]=='-')
    					B2[i]=B2[i]|(1<<j);
    		scanf("%s",str);
    		for (int j=0;j<n;j++)
    			if (str[j]=='+')
    				F1[i]=F1[i]|(1<<j);
    			else
    				if (str[j]=='-')
    					F2[i]=F2[i]|(1<<j);
    	}
    	for (int i=0;i<=(1<<n)-1;i++)
    		Dist[i]=inf;
    	int h=1,t=0;
    	Q[1]=(1<<n)-1;//开始时所有错误都存在
    	Dist[(1<<n)-1]=0;
    	inqueue[(1<<n)-1]=1;
    	do
    	{
    		t++;
    		int u=Q[t];
    		inqueue[u]=0;
    		for (int i=1;i<=m;i++)
    		{
    			unsigned int now=u;
    			if (((now&B1[i])==B1[i]) && (((~now)&B2[i])==B2[i]))//要满足B1[i]中的错误都存在且B2[i]中的错误都不存在才能加入该补丁
    			{
    				now=now&(~F2[i]);//先去掉该补丁修复的错误
    				now=now|F1[i];//再加上该补丁加上的错误
    				if (Dist[now]>Dist[u]+Cost[i])//更新最优值
    				{
    					Dist[now]=Dist[u]+Cost[i];
    					if (inqueue[now]==0)
    					{
    						h++;
    						Q[h]=now;
    						inqueue[now]=1;
    					}
    				}
    			}
    		}
    	}
    	while (h!=t);
    	if (Dist[0]==inf)//注意输出无解
    		cout<<0<<endl;
    	else
    		cout<<Dist[0]<<endl;
    	return 0;
    }
    
  • 相关阅读:
    Eclipse配置Lifery SDK步骤与错误解决。
    Java Math floor round ceil 函数
    jpa多表leftjoin 查询,自定义返回结果
    saml2协议sp-initial登录过程
    httpclientutil
    解决samlexception-inresponsetofield-of-the-response-doesnt-correspond-to-sent-mess
    spring boot 整合saml2
    使用redis防止重复提交
    mysql转化时间戳毫秒到秒
    javamail 附件以及正文加图片
  • 原文地址:https://www.cnblogs.com/SYCstudio/p/7291338.html
Copyright © 2011-2022 走看看