版权声明:本博客内容基本为原创,如有问题欢迎联系,转载请注明出处 https://blog.csdn.net/qq_41955236/article/details/82683552
题目链接:https://loj.ac/problem/6009
题意我就不讲了,都是中文题面,看得懂就行,做法的重点是如何处理必须包含错误和必须不包含错误以及对错误的弥补和增加新错误的处理。
首先是怎么处理必须要包含的错误,因为只有20个错误,所以很明显我们可以把错误转化为二进制来处理(最好事先保存下每一位二进制数的大小哦,方便之后的处理,0为未补的,1为已补的题目)
如何得到目标状态呢,下面开始处理。
1.必须要包含的意思是这些题目必须为0 ,那么如果我们让这些题目为1,当我们与目标数字&的时候如果有值,那么要求的题目一定有1,那么不符合;2.必须不包含的意思是这些题目必须为1,相同的处理,当我们与目标数字&的时候如果值为这个数,那就是符合的;3.如何对错误进行弥补,因为如果现在为0,那么会变成1,如果为1那么也还是1,所以是一个或的关系,那么我们就可以让这个问题的位为1然后进行|;4.如何产生新的错误,如果这一位原先为1,那么变为0,如果为0,那么还是0,所以是一个与的关系,但是我们不能让要出错的位是1,而是让不为该位的数为1,这样的话就不会影响原来修好的问题再出错。
得到目标状态之后怎么进行处理呢,很明显这道题虽然是用位来处理,但是是不能状压的,因为一个状态的来源可能有多个,所以就要换一个思路,用最短路做,这个是可行的,不断的更新状态。可以用spfa来做,上代码了。
#include<bits/stdc++.h>
using namespace std;
const int maxn=(1<<20);
int exneed[125],cantin[125],fix[125],addw[125];
int n,m,bits[30],ti[125],dist[maxn+5],in[maxn+5];
char s[55];
void spfa(){
queue<int> q;
memset(dist,125,sizeof(dist));
memset(in,0,sizeof(in));
in[0]=1;dist[0]=0;
q.push(0);
while(!q.empty()){
int now=q.front();q.pop();
in[now]=0;
for(int i=1;i<=m;i++){
int tmp1=now&cantin[i],tmp2=now&exneed[i];
//printf("tmp1=%d tmp2=%d\n",tmp1,tmp2);
if((tmp1!=cantin[i])||tmp2) continue;
int aim=now|fix[i]; aim&=addw[i];
if(dist[aim]-ti[i]>dist[now]){
dist[aim]=dist[now]+ti[i];
if(!in[aim]){
q.push(aim);
in[aim]=1;
}
}
}
}
}
int main(){
bits[0]=1;
for(int i=1;i<30;i++)
bits[i]=bits[i-1]*2;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%s",&ti[i],s+1);
for(int j=1;j<=n;j++){
if(s[j]=='+') exneed[i]|=bits[j-1];
if(s[j]=='-') cantin[i]|=bits[j-1];
}
scanf("%s",s+1);
for(int j=1;j<=n;j++){
if(s[j]!='+') addw[i]|=bits[j-1];//存的是除了不能被修复的补丁,之后直接&
if(s[j]=='-') fix[i]|=bits[j-1];//存的是可被修复的补丁,之后直接|
}
}
spfa();
if(dist[bits[n]-1]>1e9+7) printf("0\n");
else printf("%d\n",dist[bits[n]-1]);
return 0;
}