洛谷 题解 UVA658 【这不是bug,而是特性 It's not a Bug, it's a Feature!】

时间:2023-02-11 20:04:37

【题意】

补丁在修正\(BUG\)时,有时也会引入新的\(BUG\),假定有\(n(n<=20)\)个潜在\(BUG\),和\(m(m<=100)\)个补丁,每个补丁用两个长度为\(n\)的字符串表示,其中字符串的每个位置表示一个\(BUG\),第一个串表示打补丁之前的状态 (“-”表示该\(BUG\)必须不存在,“+”表示该补丁必须存在,0表示无所谓),第二串表示打补丁之后的状态 ("-"表示不存在,"+"表示存在,"0"表示不变)。每个补丁有一定的执行时间,你的任务是用最小的时间把所有BUG都存在的软件变得没有\(BUG\)

【算法】

\(\text{隐式图}\)\(SPFA\)

【分析】

在任意时刻,每个\(BUG\)可能存在也可能不存在,所以可以用\(n\)位二进制串来表示当前软件的状态。打完补丁之后,软件的BUG状态会发生改变,对应状态转移。是不是很像动态规划?可惜动态规划是行不通的,因为状态经过多次转移之后可能会回到以前的状态,即状态图并不是DAG。如果直接用记忆化搜索,会出现无限递归

正确的方法是把状态看成点,状态转移看成边,转化成图论中的最短路径问题,然后使用\(Dijkstra\)\(Bellman-Ford\)算法进行求解。不过这道题和普通的最短路径问题不一样:节点很多,有\(2^n\)个,而且很多状态根本遇不到(即不管怎么打补丁,也不可能打成那种状态),所以没有必要先将原图存储好

孩子咳嗽老不好, 怎么办呢?

这里介绍一种 "隐式图" 的方法,当需要得到某个点的所有边时,不是去读\(G[u]\),而是直接枚举这\(m\)个补丁是否打的上。不管是\(Dijkstra\)还是\(Bellman-Ford\)算法,这个方法都适用。

  • 一些本题的其他小技巧

得到\(x\)的二进制右起第\(i\)位:x>>(i-1)&1

\(x\)二进制的右起第\(i\)位替换为\(a\)(\(a\)\(0\)\(1\)):x^=(x&(1<<(i-1)))^(a<<(i-1))

【代码】

思路也说得很清楚了,这里就不写注释了

#include<bits/stdc++.h>
using namespace std;
const int MAXN=20+10,MAXM=100+10;
int n,m;
struct Node
{
    int t;
    int a[MAXN];
    int b[MAXN];
}patch[MAXM];
int d[2000000];
int T;
inline void init(int k)
{
    cin>>patch[k].t;
    string s;
    cin>>s;
    for(int i=0;i<s.size();i++)
    {
        if(s[i]=='-')patch[k].a[i+1]=-1;
        else if(s[i]=='0')patch[k].a[i+1]=0;
        else patch[k].a[i+1]=1;
    }
    cin>>s;
    for(int i=0;i<s.size();i++)
    {
        if(s[i]=='-')patch[k].b[i+1]=-1;
        else if(s[i]=='0')patch[k].b[i+1]=0;
        else patch[k].b[i+1]=1;
    }
}
inline bool check(int sum,int k)
{
    for(int i=1;i<=n;i++)
    {
        if(patch[k].a[i]==0)continue;
        if(patch[k].a[i]==-1 && (sum>>(n-i)&1)==0 )continue;
        if(patch[k].a[i]==1 && (sum>>(n-i)&1)==1 )continue;
        return 0;
    }
    return 1;
}
inline int get(int sum,int k)
{
    for(int i=1;i<=n;i++)
    {
        if(patch[k].b[i]==0)continue;
        if(patch[k].b[i]==-1)sum^=(sum&(1<<(n-i)))^(0<<(n-i));
        else sum^=(sum&(1<<(n-i)))^(1<<(n-i));
    }
    return sum;
}
inline void SPFA()
{
    memset(d,0x3f,sizeof(d));
    queue<int>q;
    q.push((1<<n)-1);
    d[(1<<n)-1]=0;
    while(q.size())
    {
        int now=q.front();
        //cout<<now<<endl;
        q.pop();
        for(int i=1;i<=m;i++)
        {
            if(!check(now,i))continue;
            int x=get(now,i);
            //cout<<x<<endl;
            if(d[now]+patch[i].t<d[x])
            {
                d[x]=d[now]+patch[i].t;
                q.push(x);
            }
        }
    }
}
int main()
{
    //ios::sync_with_stdio(false);
    while(cin>>n>>m)
    {
        if(n==0&&m==0)break;
        T++;
        for(int i=1;i<=m;i++)
            init(i);
        //cout<<patch[1].a[1]<<" "<<patch[1].a[2]<<" "<<patch[1].a[3]<<endl;
        SPFA();
            printf("Product %d\n",T);
        if(d[0]==0x3f3f3f3f)
            printf("Bugs cannot be fixed.\n");
        else
            printf("Fastest sequence takes %d seconds.\n",d[0]);
        cout<<endl;
    }
    return 0;
}

刘汝佳大法好!