题意: 略过。
思路: 因为n 的最大为 20 所以我们可以二进制枚举状态。
第一个字符串:
将+的地方保存进入now [1][i] 表示现在第i 个补丁 第j 位 有没有+ 有 + 则 第j 位为1
将+和0的地方保存进入now [0][i] 表示 第i 个补丁 的第j 位 有没有 - 如果有- 那么该位为 0 否则 表示该位 有+ 或者0第二个字符串:
同上,分别保存进入 aft[1][i] 和 aft[0][i]
那么我们枚举状态怎么枚举呢?
首先开始的状态肯定是全是1的 状态 就是 st=(1<<n)-1 那么怎么转移到下一个状态呢 我们就要枚举 m 个补丁的状态
如果 当前状态 第j 位 是- 那么 第i个补丁的第j 位就不能是 + 所以就是(u |now[1][i]) == u ;
如果当前状态 第j 为 + 那么第i 个补丁的第j 位 就不能是 - 所以就是 (u &now[0][i]) == u ;
这样就可以转移到下一个状态。
代码:
#include<stdio.h>
#include<string.h>
#include<queue>
#include<iostream>
#include<algorithm>
#define N 105
using namespace std;
typedef long long ll;
const int inf =0x3f3f3f3f;
const int maxn= (1<<20)+15;
int dis[maxn];
int vis[maxn];
int w[N];
int now[2][N];
int aft[2][N];
int n,m;
string s1,s2;
void spfa()
{
int st=(1<<n)-1;
int en=0;
for(int i=0;i<st;i++){
dis[i]=inf;
vis[i]=0;
}
dis[st]=0;
vis[st]=1;
queue< int >q;
q.push(st);
while(!q.empty())
{
int u=q.front(); q.pop();
//printf("%d\n",u);
vis[u]=0;
for(int i=1;i<=m;i++)
{
if((u |now[1][i]) == u && ((u &now[0][i]) == u))
{
int v=u;
v = v | aft[1][i];
v = v & aft[0][i];
if(dis[v]>dis[u]+w[i])
{
//printf("v: %d\n",v);
dis[v]=dis[u]+w[i];
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
}
if(dis[0]==inf)
{
cout<<"Bugs cannot be fixed."<<endl;
}
else
{
printf("Fastest sequence takes %d seconds.\n",dis[0]);
}
return ;
}
int main()
{
int kk=0;
while(cin>>n>>m)
{
memset(now,0,sizeof(now));
memset(aft,0,sizeof(aft));
if(n==0&&m==0) break;
for(int i=1;i<=m;i++)
{
cin>>w[i]>>s1>>s2;
for(int j=0;j<s1.length();j++)
{
if(s1[j]=='+') now[1][i]+=(1<<j);
if(s1[j]!='-') now[0][i]+=(1<<j);
if(s2[j]=='+') aft[1][i]+=(1<<j);
if(s2[j]!='-') aft[0][i]+=(1<<j);
}
}
printf("Product %d\n",++kk);
spfa();
printf("\n");
}
}