P2761 软件补丁问题

软件补丁问题–网络流24题

题意:

对于每一个软件,有特定的使用方法,简单来说,就是
软件只有必须有某些漏洞但是有没有某一些漏洞的时候才会有用
在使用补丁的同时,会消去某一些漏洞,但是同时也会引入某些漏洞
一开始有 n 个漏洞
输入的是 n n m ,就是有 m m 个补丁(每个补丁可以重复使用)
m 行会有 2 2 个字符串:
第 1 个字符串中,如果第 k 个字符 bk 为“+”,则表示第 k 个错误属于 B1[i] B 1 [ i ] ,若为“-”,则表示第 k 个错误属于 B21[i] B 21 [ i ] ,若为“0”,则第 k k 个错误既不属于 B1[i]也不属于 B2[i] B 2 [ i ] ,即软件中是否包含第 k k 个错误并不影响补丁 i 的可用性。

第 2 个字符串中,如果第 k 个字符 bk b k 为“-”,则表示第 k 个错误属于 F1[i] F 1 [ i ] ,若为“+”,则表示第 k 个错误属于 F2[i] F 2 [ i ] ,若为“0”,则第 k k 个错误既不属于 F1[i]也不属于 F2[i] F 2 [ i ] ,即软件中是否包含第 k k 个错误不会因使用补丁i 而改变。

分析:

其实这道题我不知道为什么 24,DP 是 网 络 流 24 题 , 其 实 就 是 状 态 压 缩 D P
mark01,dp[mark]! 我 们 用 m a r k 表 示 01 分 别 表 示 是 否 还 有 病 毒 , 而 d p [ m a r k ] 表 示 最 小 代 价 !
那么这道题好像有后效性,那么我们可以用 SPFA S P F A 来跑

位运算:

考验大家的其实就是位运算的能力:
我们有一个
mask_have[i]:=表示必须有哪一些病毒
mask_not[i]:=表示必须没有那些病毒
那么我们判断只要
这样:

// "~" 就是取反的标记,即二进制的 0 -> 1 , 1 -> 0
if((mask_not[i] & (~stand)) != mask_not[i]) continue;
if((mask_have[i] & stand ) != mask_have[i]) continue;

代码:

#pragma GCC optimize(3)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#define N 25
#define M 115
#define INF 0x3f3f3f3f
#define P pair<int,int>
using namespace std;

int n, m, a[M], b[M], c[M], dp[1 << (N - 2)], mask_have[M], mask_not[M];
int vis[1 << (N - 2)], kill[M], bring[M];
char s1[M][N], s2[M][N];
std::queue<P> G;

inline void init(int x)
{
    for(register int i = n - 1; i >= 0; --i)
    {
        switch(s1[x][i])
        {
            case '+': mask_have[x] |= 1 << i; break;
            case '-': mask_not[x] |= 1 << i; break;
        }
        switch(s2[x][i])
        {
            case '+': bring[x] |= 1 << i; break;
            case '-': kill[x] |= 1 << i; break;
        }
    }
}
int main()
{
    scanf("%d%d", &n, &m);
    memset(b, 0, sizeof b);
    memset(c, 0, sizeof c);
    for(register int i = 1; i <= m; ++i)
    {
        scanf("%d%s%s", a + i, s1[i], s2[i]);
        init(i);
    }
    memset(dp, INF , sizeof dp);
    dp[(1 << n) - 1] = 0; vis[(1 << n) - 1] =  1;
    G.push(P( (1 << n) - 1 , 0)); 
    while( !G.empty() )
    {
        P now = G.front(); G.pop();
        int stand = now.first;
        vis[stand] = 0;
        for(register int i = 1; i <= m; ++i)
        {
            if((mask_not[i] & (~stand)) != mask_not[i]) continue;
            if((mask_have[i] & stand ) != mask_have[i]) continue;
            int to = (stand & (~kill[i])) | bring[i];
            if(dp[to] <= dp[stand] + a[i]) continue;
            dp[to] = dp[stand] + a[i];
            if(!vis[to]) 
            {
                vis[to] = true;
                G.push(P(to , dp[to]));
            } 
        }
    }
    printf("%d\n", dp[0] == INF ? 0 : dp[0]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值