【uva 658】It's not a Bug, it's a Feature!(图论--Dijkstra或spfa算法+二进制表示+类“隐式图搜索”)...

题意:有N个潜在的bug和m个补丁,每个补丁用长为N的字符串表示。首先输入bug数目以及补丁数目。然后就是对M个补丁的描述,共有M行。每行首先是一个整数,表明打该补丁所需要的时间。然后是两个字符串,第一个字符串是对软件的描述,只有软件处于该状态下才能打该补丁该字符串的每一个位置代表bug状态("-"代表该位置没bug,"+"代表该位置有bug,"0"表示该位置无论有没有bug都可打补丁)。然后第二个字符串是对打上补丁后软件状态的描述"-"代表该位置上的bug已经被修复,"+"表示该位置又引入了一个新的bug, "0"表示该位置跟原来状态一样)。要求用最少时间完成对软件的修复,即将所有位置全都置为0。(N≤20)

解法:对每个bug用二进制表示,补丁两端的点通过边来转换,共2^n-1个结点。由于这样的点太多了,而实际上我们不需要这么多点。于是便像“隐式图搜索”一样,不知道所有的点,便通过所有的边来拓展。在这题中,也就是不是像平常一样用邻接表来拓展,而是枚举所有补丁,看是否能用上这条边。(注意"0"既可表示"+",也可表示"-")这样就可以从源点“11...11”开始用Dijkstra边求解边建图,直到"00...00"。

注意——由于用到了二进制的位运算,该加括号的地方千万不要漏;Dijkstra中优先队列的top()是汇点的值时就可以跳出,因为剩下的可以拓展的点都比它的值大,由于没有负权边,就不可能通过一些边又比它小了。

另外,本来我想偷懒不分析时间复杂度的,但是!!(o´・ェ・`o)我发现Dijkstra+优先队列的算法的时间是spfa的好几倍啊!于是我还是分析了一下......
Dijkstra+优先队列:由于是类“隐式图搜索”,所以每个点进行拓展的操作都是m。原先的时间复杂度是O(n log n+m),其中加的m就是每个点拓展时总共访问过一次的边。而此时这里要+km,k表示在汇点出队前入队的点树,因为每个点拓展时都遍历了一次m条所有的边啊。因此总的是O(n log n+km),最坏情况O(n log n+nm),而最坏的点是2^n。o(─.─|||
spfa:原来的时间复杂度是O(km),k为每个节点进入队列的次数,且k一般<=2。而这里尽管每个点拓展时遍历了所有边,但依旧没有使它的复杂度改变多少!!!∑(゚Д゚ノ)ノ 因为它本来主要承担这个时间复杂度的就是这个"km"啊!
总的来说就是——类“隐式图搜索”还是用spfa吧。

你们可以看看代码感受一下~ ╮(╯_╰)╭ P.S.我打得真的好粗心,所以注释有一点点儿多啊~

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<queue>
 6 using namespace std;
 7 
 8 const int N=25,NN=1<<20,M=105,INF=(int)2e9;
 9 int n,m;
10 int d[NN],vis[NN];
11 struct patch{int d;char s[N],ss[N];}a[M];
12 
13 struct node
14 {
15     int bug,d;
16     node() {}
17     node(int u,int v) {bug=u;d=v;}
18     bool operator < (const node& now) const
19     { return d>now.d; }//return!!!
20 };
21 priority_queue<node> q;
22 
23 bool check(int x,int k)
24 {
25     for (int i=0;i<n;i++)
26     {
27       int tmp=1<<i;//反着看二进制也没什么不行的(110表示一般的011),只要没有搞错位数就好了
28       if (a[k].s[i]=='+'&& !(x&tmp)) return false;
29       if (a[k].s[i]=='-'&& (x&tmp)) return false;//()
30     }
31     return true;
32 }
33 int trans(int x,int k)
34 {
35     int full=(1<<n)-1;//()
36     for (int i=0;i<n;i++)
37     {
38       int tmp=1<<i;
39       if (a[k].ss[i]=='+') x|=tmp;
40       if (a[k].ss[i]=='-') x&=(full-tmp);//&(full-tmp) 或 & ~tmp
41     }
42     return x;
43 }
44 int solve()
45 {
46     int tmp=(1<<n)-1;//node:0~tmp
47     while (!q.empty()) q.pop();
48     for (int i=0;i<=tmp;i++) d[i]=INF,vis[i]=0;
49     node st=node(tmp,0);
50     q.push(st); d[tmp]=0;
51     while (!q.empty())
52     {
53       int x=q.top().bug; 
54       if (!x) return d[0];//notice,别的再更新也是从大于它的d[x]开始更新,无更优的解了
55       q.pop();
56       if (vis[x]) continue;
57       vis[x]=1;
58       for (int i=1;i<=m;i++)
59       {
60         if (!check(x,i)) continue;
61         int y=trans(x,i);
62         if (d[x]+a[i].d<d[y])
63         {
64           d[y]=d[x]+a[i].d;
65           q.push(node(y,d[y]));
66         }
67       }
68     }
69     if (d[0]!=INF) return d[0];
70     return -1;
71 }
72 int main()
73 {
74     int T=0;
75     while (1)
76     {
77       scanf("%d%d",&n,&m);
78       if (!n && !m) break;
79       for (int i=1;i<=m;i++)
80         scanf("%d%s%s",&a[i].d,a[i].s,a[i].ss);
81       int ans=solve();
82       printf("Product %d\n",++T);
83       if (ans==-1) printf("Bugs cannot be fixed.\n");
84       else printf("Fastest sequence takes %d seconds.\n",ans);
85       printf("\n");
86     }
87     return 0;
88 }
Dijkstra+优先队列
 1 #include <cstdio>
 2 #include <queue>
 3 #include <cstring>
 4 #include <iostream>
 5 #include <cstdlib>
 6 #include <algorithm>
 7 #include <vector>
 8 #include <map>
 9 #include <string>
10 #include <set>
11 #include <ctime>
12 #include <cmath>
13 #include <cctype>
14 using namespace std;
15 #define MAX 100000
16 const int maxn = 110;
17 const int INF = 0x3f3f3f3f;
18 #define LL long long
19 int vis[(1<<20)+10];
20 int d[(1<<20)+10];
21 int s[2][maxn];
22 int t[2][maxn];
23 string s1,s2;
24 int n,m,w[maxn];
25 void dij()
26 {
27     int Max=(1<<n)-1;
28     queue<int>q;
29     for (int i = 0;i<Max;i++)
30     {
31         vis[i]=0;
32         d[i]=INF;
33     }
34     d[Max]=0;
35     q.push(Max);
36     int u,v;
37     while (!q.empty())
38     {
39         u=q.front();
40         vis[u]=0;
41         q.pop();
42         for (int i = 0;i<m;i++)
43         {
44             if ((u | s[1][i])== u && (u & (s[0][i]))==u)
45             {
46                 v=u;
47                 v = v | t[1][i];
48                 v = v & t[0][i];
49                 if (d[u]+w[i] <d[v])
50                 {
51                     d[v]=d[u]+w[i];
52                     if (!vis[v])
53                     {
54                         q.push(v);
55                         vis[v]=1;
56                     }
57                 }
58             }
59         }
60     }
61     if (d[0]==INF)
62         printf("Bugs cannot be fixed.\n");
63     else
64         printf("Fastest sequence takes %d seconds.\n",d[0]);
65 }
66 int cas=1,T;
67 
68 int main()
69 {
70     while (scanf("%d%d",&n,&m)!=EOF && n)
71     {
72         memset(s,0,sizeof(s));
73         memset(t,0,sizeof(t));
74         for (int i = 0;i<m;i++)
75         {
76             cin >> w[i] >> s1 >> s2;
77             for (int j = 0;j<n;j++)
78             {
79                 if (s1[j]=='+')
80                     s[1][i]+=(1<<j);
81                 if (s1[j]!='-')
82                     s[0][i]+=(1<<j);
83                 if (s2[j]=='+')
84                     t[1][i]+=(1<<j);
85                 if (s2[j]!='-')
86                     t[0][i]+=(1<<j);
87             }
88         }
89         printf("Product %d\n",cas++);
90         dij();
91         printf("\n");
92     }
93     return 0;
94 }
spfa(来源于网上)

 

转载于:https://www.cnblogs.com/konjak/p/6036687.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值