T 公司发现其研制的一个软件中有 个错误,随即为该软件发放了 个补丁程序。
每一个补丁程序都有其特定的适用环境,某个补丁只有在软件中包含某些错误而同时又不包含另一些错误时才可以使用。一个补丁在排除某些错误的同时,往往会加入另一些错误。
换句话说,对于任意一个补丁 ,都有四个与之相应的集合 和 。仅当软件包含 中的所有错误,而不包含 中的任何错误时,才可以使用补丁 。补丁 将修复软件中的某些错误集合 ,而同时加入另一些错误 。另外,运行每个补丁都耗费一定的时间。
试设计一个算法,利用 T 公司提供的 个补丁程序将原软件修复成一个没有错误的软件,并使修复后的软件耗时最少。对于给定的 个错误和 个补丁程序,找到总耗时最少的软件修复方案。
第一行有两个正整数 和 。 表示错误总数,表示补丁总数。
接下来 行给出了 个补丁的信息。每行包括一个正整数,表示运行补丁程序 所需时间,以及两个长度为 的字符串。中间用一个空格符隔开。
第一个字符串中,如果第 个字符为 +
,则表示第 个错误属于 。若为 -
,则表示第 个错误属于 。若为 0
,则第 个错误既不属于 也不属于 ,即软件中是否包含第 个错误并不影响补丁 的可用性。
第二个字符串中,如果第 个字符为 -
,则表示第 个错误属于 。若为 +
,则表示第 个错误属于 。若为 0
,则第 个错误既不属于 也不属于 ,即软件中是否包含第 个错误不会因使用补丁 而改变。
程序运行结束时,将总耗时数输出。如果问题无解,则输出 0
。
对于 的数据:,。
给定一个具有个特征的状态,和两个字符串、来表示的信息,当且仅当当前的状态具有中的所有状态,不含有的任何状态时候;能够在t的代价下将中的去掉,引入,求从初始状态全为1到最终状态全为0所需要的最小代价
涉及到状态很容易就想到状态压缩,我们定义当前状态中二进制位下表示含有当前错误,.
对于给出的字符串,我们将他压为一个二进制数,中(+)表示要含有当前错误,中(-)表示不能有当前错误
那么显而易见,当前状态能使用此补丁的充要条件为:
&
并且
&
对于给出的字符串,同样做二进制化操作,中的代表会消除在这个位置上的,中的表示会带来在这个位置上的,那么经过了我们处理过后,状态会变成:
首先带来的错误,
其次消除的错误,^
(因为f1中所有的错误都会被消除,所以大可直接假设中含有所有f1的错误再消除)
既然要求最小的花费,又涉及到状态与状态之间转移的代价,那么很容易就想到用最短路,这里我用的是,应该也是没问题的,但是没有尝试过。先把处理好的初始状态入队,再按照正常的来做就行了,这里提一嘴个人很容易打错的点,就是只有转移的状态既能够带来更优的结果
并且当前状态没有在更新队列里面
的时候我们才将它入。这也是为什么q.push(v),vis[v]=1;
放在了松弛条件if(dis[x]+p[i].w<dis[v])
的里面
点击查看代码
| void spfa() |
| { |
| memset(dis,0x7f,sizeof dis); |
| dis[start]=0;q.push(start);vis[start]=1; |
| while(!q.empty()) |
| { |
| int x=q.front(); |
| for(int i=1;i<=m;i++) |
| { |
| if(usable(x,i)) |
| { |
| int v=trans(x,i); |
| if(dis[x]+p[i].w<dis[v]) |
| { |
| dis[v]=dis[x]+p[i].w; |
| if(!vis[v]) q.push(v),vis[v]=1;; |
| } |
| } |
| } |
| vis[x]=0,q.pop(); |
| } |
| } |
点击查看代码
| #include<iostream> |
| #include<cstdio> |
| #include<algorithm> |
| #include<cstring> |
| #include<queue> |
| #define maxn 100000 |
| using namespace std; |
| struct fix{int b1,b2,f1,f2,w;}p[maxn]; |
| int n,m;char c[maxn];int start,end=0; |
| bool usable(int x,int i){return ((x&p[i].b1)==p[i].b1)&&(!(x&p[i].b2));} |
| int trans(int x,int i){return ((x|p[i].f2)|p[i].f1)^p[i].f1;} |
| void pre() |
| { |
| scanf("%d%d",&n,&m); |
| start=(1<<n)-1; |
| for(int i=1;i<=m;i++) |
| { |
| scanf("%d",&p[i].w); |
| scanf("%s",c);int len=strlen(c); |
| for(int j=0;j<len;j++) |
| { |
| p[i].b1<<=1,p[i].b2<<=1; |
| if(c[j]=='+')p[i].b1+=1; |
| else if(c[j]=='-')p[i].b2+=1; |
| } |
| scanf("%s",c);len=strlen(c); |
| for(int j=0;j<len;j++) |
| { |
| p[i].f1<<=1,p[i].f2<<=1; |
| if(c[j]=='-')p[i].f1+=1; |
| else if(c[j]=='+')p[i].f2+=1; |
| } |
| } |
| } |
| |
| int dis[1<<22],vis[1<<22];queue<int>q; |
| void spfa() |
| { |
| memset(dis,0x7f,sizeof dis); |
| dis[start]=0;q.push(start);vis[start]=1; |
| while(!q.empty()) |
| { |
| int x=q.front(); |
| for(int i=1;i<=m;i++) |
| { |
| if(usable(x,i)) |
| { |
| int v=trans(x,i); |
| if(dis[x]+p[i].w<dis[v]) |
| { |
| dis[v]=dis[x]+p[i].w; |
| if(!vis[v]) q.push(v),vis[v]=1;; |
| } |
| } |
| } |
| vis[x]=0,q.pop(); |
| } |
| } |
| |
| int main() |
| { |
| pre(); |
| spfa(); |
| if(dis[0]==dis[1<<22-1]) |
| printf("0"); |
| else |
| printf("%d",dis[0]); |
| return 0; |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效