题意:给定n个bug(n个位置全是bug),m个补丁。对于每个补丁的功能描叙有规定:补丁花费的时间 打补丁前的状态 打补丁后的状态。其中“状态”用一个字符串描叙,第I位代表第I个bug的状态。
前:0代表有无对补丁无影响,-这个位置不能有bug,+这个位置必须有bug。
后:0代表补丁对这个位置不起作用,原来是怎样,打补丁后还是怎样。-:该位置的bug被修补,如果没有bug不起作用,+:该补丁在这个位置处引入bug。
每个补丁可以重复使用,求用最短的时间,将所有bug修复,如果不能做到,输出特定字符串。
思路:bug数不超过20个,用整型可以存储20个位,考虑用1代表这个位置有bug,0表示无bug。开始的状态是(1<<n)-1,所有位均为1,视作一个顶点。则终点是全0,即0。为了方便处理,将输入的补丁倒序。对于每个状态,试探所有补丁,满足条件则转移,因为打了补丁可能回到之前的状态,不能用动态规划。用dijstra求最短路即可。
看错条件,将n看作1=<n<=10,找了很久才发现= =。结果是疯狂re。很经典的一道题。
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
const int maxm = 110;
using namespace std;
struct Pt
{
char pre[maxm];
char num[maxm];
int t;
}pt[maxm];
struct node
{
int x,d;
bool operator < (const node &b)const
{
return d>b.d;
}
};
int d[1<<22],vis[1<<22];
int n,m;//the number of bugs and patches 1=<n<=20
int dijstra(int s)
{
priority_queue<node,vector<node> > q;
for(int i=0;i<(1<<22);i++)d[i]=1<<30;
d[s]=0;
q.push(node{s,0});
int u;
memset(vis,0,sizeof(vis));
while(!q.empty())
{
node a=q.top();q.pop();
u=a.x;
if(vis[u])
continue;
vis[u]=1;
int v;
for(int i=0;i<m;i++)
{
v=u;
int flag=1;
int sj;
for(int j=0;j<n;j++)
{
sj= (v)&(1<<j);
if(pt[i].pre[j]=='-'&&sj)
{
flag=0;break;
}
if(pt[i].pre[j]=='+'&&sj==0)
{
flag=0;break;
}
}
if(flag)
{
for(int j=0;j<n;j++)
{
sj=(v)&(1<<j);
if(pt[i].num[j]=='-'&&sj)
{
v=v^(1<<j);
}
if(pt[i].num[j]=='+')
{
v=v|(1<<j);
}
}
if(d[v]>d[u]+pt[i].t)
{
d[v]=d[u]+pt[i].t;
q.push(node{v,d[v]});
}
}
}
}
}
void Reverse(char * s)
{
for(int i=0;i<n/2;i++)
{
char temp=s[i];
s[i]=s[n-1-i];
s[n-1-i]=temp;
}
}
int main()
{
//freopen("in.txt","r",stdin);
int cas=0;
while(scanf("%d%d",&n,&m)&&(n+m))
{
int t;
for(int i=0;i<m;i++)
{
scanf("%d",&pt[i].t);
scanf("%s",&pt[i].pre);
Reverse(pt[i].pre);
scanf("%s",&pt[i].num);
Reverse(pt[i].num);
}
int cur = (1<<n) - 1;//1代表bug
dijstra(cur);
printf("Product %d\n",++cas);
if(d[0]!=1<<30)
printf("Fastest sequence takes %d seconds.\n",d[0]);
else
printf("Bugs cannot be fixed.\n");
printf("\n");
}
}