【题解】软件补丁问题 ( SPFA+状压)

软件补丁问题

题目传送门:洛谷 P2761 软件补丁问题

题目背景

none!

题目描述

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

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

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

输入格式

第 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 而改变。

输出格式

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

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

none!

思路:SPFA+状态压缩

由题意得知,是给你初始状态和最终状态,让你求初始状态到最终状态的花费的最少时间

很显然 可以用bfs来写 而每次状态转移需要的时间并不是1,所以我们选择用spfa跑一遍dij也行

那么 这一个个状态用数组肯定不好存,并且这一个个的bug只有被修复和没有被修复两个状态

所以,我们考虑用二进制来存储状态,也算是状态压缩

怎么状态压缩

在d[110]里,110表示这个软件第2个和第三个bug还没有被修复的状态

d[i]表示从初始状态到i状态所需要的最少时间

在补丁f1里面,1表示可以修复哪个bug,0表示不能修复哪个bug

用 (t|f1[i]) ^ f1[i] 来表示使用第i个补丁来修复bug 要先或再异或为了让f1[i]中多修复的bug先并到t中去,再异或,正好消掉这个多修复的bug为0。再或f2[i]把这个补丁带来的bug并到t状态里面去

那么如何跑spfa呢
  • 把初始状态看作起点,最终状态看作终点

  • 本题里每次选择使用的补丁就是最短路里面每次我们选择的四个方向 然后改变状态

  • 每次使用补丁消耗的时间就是最短路里面的边权

然后我们成功的跑起spfa

为什么网络流24题里面没有网络流,
可能是因为
image

代码实现:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
queue<int>q;
int n,m,b1[200],b2[200],f1[200],f2[200],d[1<<20],v[200];
bool st[1<<20];
int bfs()
{
    q.push((1<<n)-1);
    memset(d,0x3f,sizeof d);
    d[(1<<n)-1]=0;
    
    
    while(q.size())
    {
        int t=q.front();q.pop();
        st[t]=0;
        for(int i=1;i<=m;i++)
        {
            if((t&b2[i])!=0||(t&b1[i])!=b1[i]) continue;
            int ver=((t|f1[i])^f1[i])|f2[i];
            if(d[ver]>d[t]+v[i])
            {
                d[ver]=d[t]+v[i];
                if(!st[ver])
                {
                    st[ver]=true;
                    q.push(ver);
                }
            }
        }
        
        
    }
    
    return d[0];
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        string a,b;
        cin>>v[i]>>a>>b;
        
        for(int j=0;j<n;j++)
        {
            if(a[j]=='+') b1[i]|=(1<<j);
            else if(a[j]=='-') b2[i]|=(1<<j);
        }
        for(int j=0;j<n;j++)
        {
            if(b[j]=='-') f1[i]|=(1<<j);
            else if(b[j]=='+') f2[i]|=(1<<j);
        }
        
    }
    int ans=bfs();
    if(ans==0x3f3f3f3f) puts("0");
    else printf("%d\n",ans);
    
    
    return 0;
}
posted @ 2022-07-11 15:02  watasky  阅读(69)  评论(0)    收藏  举报