洛谷 P2761 软件补丁问题【状压+最短路】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/niiick/article/details/81842785

时空限制 1000ms / 128MB

题目描述

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。


题目分析

一开始想了半天的状压DP
才发现有后效性
所以有时候想问题就是能不能想太多=_=

正解是在一个结点个数为 2 n 的图上跑最短路
每个结点编号代表一个软件状态
即从二进制角度理解这个编号
第i位为0表示软件包含第i个错误为1则表示不包含
那么显然答案就是 0 ~ 2 n 1 的最短路

那么如何确定连边呢
以SPFA的过程为例(因为我写的就是SPFA)

设当前从队首取出的结点为u
依次遍历m个补丁
软件信息的b1,b2,f1,f2也从二进制理解
第i位为1则错误i属于这个集合,若0则不属于

加入判断

if( (b1&u) || (b2&(~u)) )continue;

且运算能判断b1和u是否同时有一位i为1
若b1第i位为1且u的第i位为1
则说明该软件需要错误i但u没有

而将b2 按位取反
0表示u必须不包含这个错误,1表示必须包含
进行且运算判断b2和u是否同时有一位i为1
若b2第i位为1且u的第i位为1
则说明该软件要求必须包含错误i但u不包含

所以这两种情况下这条边都不能存在

如果上面判断这条边可行
就计算u经过这条边到达的下一个点v

int v=u;
v|=rem[i].f1; v&=(~rem[i].f2);

或运算能让f1中第i位为1时将u的第i位也改为1,而u的其他位不会改变
f2 按位取反后0表示该软件会增加增加这个错误,1表示不会
那么且运算能让f2中第i位为0时将u的第i位也改为0,而u的其他位不会改变

得到u的一个后继点v后
剩下的就是继续SPAF了


#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
#include<map>
using namespace std;
typedef long long lt;

int read()
{
    int x=0,f=1;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return x*f;
}

const int inf=1128481603;
const int maxn=2000010;
int n,m;
struct node{int tim,b1,b2,f1,f2;}rem[200];
struct edge{int v,dis,nxt;}E[maxn<<2];
int head[maxn],tot;
int d[maxn],vis[maxn];
char ss[50];

void add(int u,int v,int dis)
{
    E[++tot].nxt=head[u];
    E[tot].v=v; E[tot].dis=dis;
    head[u]=tot;
}

void SPFA()
{
    queue<int> q; q.push(0);
    memset(d,67,sizeof(d)); d[0]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop(); vis[u]=0;
        for(int i=1;i<=m;++i)
        {
            int v=u;
            if((rem[i].b1&u)||(rem[i].b2&(~u)))continue;
            v|=rem[i].f1; v&=(~rem[i].f2);
            if(d[v]>d[u]+rem[i].tim)
            {
                d[v]=d[u]+rem[i].tim;
                if(!vis[v])q.push(v),vis[v]=1;
            }
        }
    }
}

int main()
{
    n=read();m=read();
    for(int i=1;i<=m;++i)
    {
        rem[i].tim=read();
        rem[i].b1=rem[i].b2=rem[i].f1=rem[i].f2=0;

        scanf("%s",ss+1);
        for(int j=1;j<=n;++j)
        if(ss[j]=='+') rem[i].b1|=1<<(j-1);
        else if(ss[j]=='-') rem[i].b2|=1<<(j-1);

        scanf("%s",ss+1);
        for(int j=1;j<=n;++j)
        if(ss[j]=='-') rem[i].f1|=1<<(j-1);
        else if(ss[j]=='+') rem[i].f2|=1<<(j-1);
    }

    SPFA();
    if(d[(1<<n)-1]==inf) printf("0");
    else printf("%d",d[(1<<n)-1]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/niiick/article/details/81842785
今日推荐