zoukankan      html  css  js  c++  java
  • uva 658 It's not a Bug, it's a Feature!

    最短路(隐式图搜索)

    题目是过了,不过时间好慢,用了2.200s。大概是没有用位运算的原因,我是直接模拟的。而解决最短路的问题是用spfa来做(感觉这种思路更加形象和容易理解,并且更符合隐式图搜索的感觉)

    来说说题意,这个题意都是很长很烦的。首先给出n和m,表示有n个bug和m个补丁。一开始存在n个bug,用1表示一个bug存在0表示不存在,所以一开始就是n个1,我们的目的是要消除所有的bug,所以目标状态就是n个0。对于每个补丁,会给出使用这个补丁的时间,另外会给出两个长度为n的字符串,第一个字符串表示这个补丁适用于什么情况下的bug,第二个字符串表示使用完这个补丁后原来的bug会变成怎么样。先说第一个字符串,s[i]='0',表示第i个bug存在与否都无所谓;s[i]='+',表示第i个bug一定要存在;s[i]='-',表示第i个bug必须不存在;能不能使用这个补丁,就要看当前bug的状态是不是能不能全部满足第一个字符串,能的话就可以使用。第二个字符串表示使用完后的情况,ss[i]='0',表示第i个bug保持不变,原来是1就1是0就0;ss[i]='+',表示第i个bug必须为1;ss[i]='-',表示第i个bug必须为0。

    最终题目要求解的就是消除所有的bug并且用时最短,输出最短时间,如果bug不可能被完全消除那么就输出失败

     

    这个题目可以转化为最短路的模型来求解。由n个1或0来表示bug,我们很容易联想要二进制和十进制的转化,对于当前的bug状态,我们可以转化为1个十进制来表示,那么一开始的状态显然就是2^n-1,目标状态就是0,也就是从2^n-1转化为0,用时最少,相当于从2^n-1到0的最短路

    对于一个补丁,其实就是一些有向边(是有向边,而且不是一条,可能是多条,所以是一些),为什么?因为对于当前的bug状态我们转化为十进制u,扫描所有的补丁,找到可以使用的补丁,并在这个补丁作用下变为一个新的bug状态,这个新的bug状态也对应一个十进制v,所以其实就是u到v,有向边u-->v,边权就是使用补丁使用的时间。

    一种思路是先建图,再来一个最短路,但是会超时,因为图的顶点太多,边也很多。n的上限是20,即顶点个数为2^20-1,而边数在最坏情况下很大的,一定会超时。而我们思考可以知道,很多顶点是不一定会经过的,也就是很多bug的状态不会出现,所以我们为什么不一边最短路一边建图呢?所以就用spfa来做。而这题边权是不会为负的,所以也可以用dij

     

    这题只要处理好细节,AC是没问题,但是想时间快点,就要用一些技巧。主要区别在于,怎么判断哪些补丁适用于当前的bug,另外使用后怎么快速得到新的bug状态等等,这些都可以用位运算来提高速度。

    另外就是如果用dij的优先队列应该会比spfa更快。

     

    另外注意一个问题,不知道是我自己的代码的问题还是本质上的错误。就是关系bug二进制的保存。好像10000,表示16,一开始我为了方便运算是逆过来保存00001,然后去计算新的状态也是逆过来保存,然后就一直TLE。后来我改回来顺着保存,就AC了。

     

    代码有点长…………

     

    #include <cstdio>
    #include <cstring>
    #include <queue>
    using namespace std;
    #define INF 0x3f3f3f3f
    #define N 25
    #define M 110
    #define MAX 1048600
    int n,m,s,t;
    int d[MAX],vis[MAX],a[N];
    int bug[N],newbug[N];
    struct patches
    {
        char b0[N],b[N];
        int t;
    };
    typedef struct patches patches;
    patches p[M];
    
    void get_bug(int x)
    {
        //memset(bug,0,sizeof(bug));
        for(int i=n-1; i>=0; i--)
        {
            bug[i]=x%2;
            x/=2;
        }
    /*
        printf("get_bug\n");
        for(int i=0; i<n; i++)
            printf("%d",bug[i]);
        printf("\n");
    */
        return ;
    }
    int can_use(int k)
    {
        int OK=1;
        for(int i=0; i<n; i++)
        {
            if(p[k].b0[i]=='+' && bug[i]==0)
            { OK=0; break;}
            else if(p[k].b0[i]=='-' && bug[i]==1)
            { OK=0; break;}
        }
        return OK;
    }
    int get_newbug(int k)
    {
        //memset(newbug,0,sizeof(newbug));
        for(int i=n-1; i>=0; i--)
        {
            if(p[k].b[i]=='0')
                newbug[i]=bug[i];
            else if(p[k].b[i]=='+')
                newbug[i]=1;
            else
                newbug[i]=0;
        }
        int val=0;
        for(int i=n-1,j=0; i>=0; i--,j++)
            val+=newbug[i]*a[j];
    /*
        printf("get_newbug\n");
        for(int i=0; i<n; i++)
            printf("%d",newbug[i]);
        printf("\n");
        printf("val=%d\n",val);
    */
        return val;
    }
    void solve()
    {
        queue <int> q;
    
        s=0; t=0;
        for(int i=0; i<n; i++) s+=a[i];
        //printf("%d %d\n",s,t);
        memset(d,0x3f,(s+10)*sizeof(int));
        memset(vis,0,(s+10)*sizeof(int));
        d[s]=0; q.push(s);  vis[s]=1;
        while(!q.empty())
        {
            int u=q.front(); q.pop(); vis[u]=0;
            get_bug(u);  //将十进制u转化为二进制保存在bug数组中
            for(int i=1; i<=m; i++)  //枚举所有补丁
            {
                if(can_use(i)) //i补丁可以对u产生作用
                {
                    int v=get_newbug(i); 
                    //得到新状态的bug,已经转化为十进制,二进制保存在newbug数组中
                    if(d[u]+p[i].t < d[v])
                    {
                        d[v]=d[u]+p[i].t;
                        if(!vis[v])
                        { 
                            vis[v]=1;
                            q.push(v);
                        }
                    }
                }
            }
        }
    
        return ;
    }
    int main()
    {
        int T=0;
        memset(a,0,sizeof(a)); a[0]=1;
        for(int i=1; i<=21; i++)
            a[i]=a[i-1]*2;
        //把2^i保存下来,以后方便使用不用多次计算
        
        while(scanf("%d%d",&n,&m))
        {
            if(!n &&!m) break;
            T++;
            for(int i=1; i<=m; i++)
            scanf("%d%s%s",&p[i].t,p[i].b0,p[i].b);
    
            solve();
            printf("Product %d\n",T);
            if(d[0]!=INF)
                printf("Fastest sequence takes %d seconds.\n",d[0]);
            else
                printf("Bugs cannot be fixed.\n");
            printf("\n");
        }
        return 0;
    }
  • 相关阅读:
    Xcode7下模拟器输入文本无法显示系统键盘的解决办法
    Mac系统下开启和关闭隐藏文件的方法
    iOS常用第三方开源框架和优秀开发者博客等
    UILabel 的一个蛋疼问题
    学习进度条
    学习进度条
    四则运算2
    学习进度条
    第一次被要求连接数据库的课堂测试
    课后作业06多态与接口
  • 原文地址:https://www.cnblogs.com/scau20110726/p/2820739.html
Copyright © 2011-2022 走看看