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


    【题意】

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

    【算法】

    ( ext{隐式图})(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
    ",T);
    		if(d[0]==0x3f3f3f3f)
    			printf("Bugs cannot be fixed.
    ");
    		else
    			printf("Fastest sequence takes %d seconds.
    ",d[0]);
    		cout<<endl;
    	}
    	return 0;
    }
    

    刘汝佳大法好!

  • 相关阅读:
    廖雪峰Java6IO编程-2input和output-1inputStream
    安装spy-debugger查看前端日志
    廖雪峰Java6IO编程-1IO基础-1IO简介
    廖雪峰Java5集合-6Stack-1使用Stack
    廖雪峰Java5Java集合-5Queue-1使用Queue
    廖雪峰Java5集合-4Set-1使用Set
    20)升级登录标志
    19)加了验证码验证
    94)图片验证码
    18)添加引号转移函数,防止SQL注入
  • 原文地址:https://www.cnblogs.com/hulean/p/11128646.html
Copyright © 2020-2023  润新知