洛谷 P2761 软件补丁问题

题目描述

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

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

试设计一个算法,利用 T 公司提供的 m 个补丁程序将原软件修复成一个没有错误的软件,并使修复后的软件耗时最少。对于给定的 n 个错误和 m 个补丁程序,找到总耗时最少的软件修复方案。

1<=n<=20, 1<=m<=100

问题分析:

由于n的范围很小,我们可以把每一个漏洞的状态用0和1来表示,即将当前系统状态用一个01的串来表示,状态压缩。

DP即可,但是由于DP状态会有1e6种,每种转移要m次,复杂度太大了。。。所以用最短路解决问题,可以减少一些不会到达的状态。

起点为所有漏洞都未被修复的状态(11.....11),终点为所有漏洞都已被修复的状态(00....00).

代码:

#include <bits/stdc++.h>
using namespace std;
char s[25];
int t[105];
int sta1[105],sta2[105];
int res1[105],res2[105];
int dis[2100000],vis[2100000];
queue<int> Q;
int main()
{
    memset(dis,0x3f,sizeof(dis));
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= m;++i){
        scanf("%d",&t[i]);
        scanf("%s",s);
        for(int j = 0;j < n;++j){
            if(s[j] == '+') sta1[i] += (1<<j);
            else if(s[j] == '-') sta2[i] += (1<<j);
        }
        scanf("%s",s);
        for(int j = 0;j < n;++j){
            if(s[j] == '+') res2[i] += (1<<j);
            else if(s[j] == '-') res1[i] += (1<<j);
        }
    }
    int now = (1<<n)-1;
    dis[now] = 0;
    Q.push(now);
    vis[now] = 1;
    while(!Q.empty()){
        now = Q.front();
        Q.pop();
    
        for(int i = 1;i <= m;++i){
            if((now & sta1[i]) != sta1[i]) continue;
            if((~now & sta2[i]) != sta2[i]) continue;
            int tmp = (now  & (~res1[i])) | res2[i];           
            if(dis[tmp] > dis[now] + t[i]){
                dis[tmp] = dis[now] + t[i];
                if(!vis[tmp]) vis[tmp] = 1,Q.push(tmp);
            }
        }
        vis[now] = 0;
    }
    if(dis[0] == 0x3f3f3f3f) puts("0");
    else printf("%d\n",dis[0]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/obob/p/9567451.html