Evanyou Blog 彩带

洛谷P2761 软件补丁问题 [状压DP,SPFA]

  题目传送门

软件补丁问题

题目描述

T 公司发现其研制的一个软件中有 n 个错误,随即为该软件发放了一批共 m 个补丁程序。每一个补丁程序都有其特定的适用环境,某个补丁只有在软件中包含某些错误而同时又不包含另一些错误时才可以使用。一个补丁在排除某些错误的同时,往往会加入另一些错误。

换句话说,对于每一个补丁 i,都有 2 个与之相应的错误集合 B1[i]和 B2[i],使得仅当软件包含 B1[i]中的所有错误,而不包含 B2[i]中的任何错误时,才可以使用补丁 i。补丁 i 将修复软件中的某些错误 F1[i],而同时加入另一些错误 F2[i]。另外,每个补丁都耗费一定的时间。

试设计一个算法,利用 T 公司提供的 m 个补丁程序将原软件修复成一个没有错误的软件,并使修复后的软件耗时最少。对于给定的 n 个错误和 m 个补丁程序,找到总耗时最少的软件修复方案。

输入输出格式

输入格式:

 

第 1 行有 2 个正整数 n 和 m,n 表示错误总数,m表示补丁总数,1<=n<=20, 1<=m<=100。

接下来 m 行给出了 m 个补丁的信息。每行包括一个正整数,表示运行补丁程序 i 所需时间,以及 2 个长度为 n 的字符串,中间用一个空格符隔开。

第 1 个字符串中,如果第 k 个字符 bk 为“+”,则表示第 k 个错误属于 B1[i],若为“-”,则表示第 k 个错误属于 B21[i],若为“0”,则第 k 个错误既不属于 B1[i]也不属于 B2[i],即软件中是否包含第 k 个错误并不影响补丁 i 的可用性。

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

 

输出格式:

 

程序运行结束时,将总耗时数输出。如果问题无解,则输出 0。

 

输入输出样例

输入样例#1: 
3 3
1 000 00-
1 00- 0-+
2 0-- -++
输出样例#1: 
8

说明

none!

 


  分析:

  说好的网络流二十四题???

  反正想了很久想不到怎么搞网络流,然后一看。。。O.O???状压?SPFA?神奇。思路倒是不难,因为$n$的范围只有$20$,所以就可以用二进制来表示当前的状态,转移的时候因为需要满足包含$B1[]$中的错误,不包含$B2[]$中的错误,那么我们也可以想到把它转化成一张图,用SPFA来转移。

  一开始把输入的定义搞混调了好久,真SA。。。

  Code:

 

//It is made by HolseLee on 27th July 2018
//Luogu.org P2761
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<queue>
using namespace std;

const int inf=0x3f3f3f3f;
int n,m,a[101],has[101],dont[101],dp[(1<<21)],kill[101],bring[101];
char s[2][21];
bool vis[(1<<21)];
queue<int>t;

void ready(int x)
{
    for(int i=n-1;i>=0;--i){
        switch (s[1][i]){
            case '+':has[x]|=(1<<i);break;
            case '-':dont[x]|=(1<<i);break;
        }
        switch (s[2][i]){
            case '+':bring[x]|=(1<<i);break;
            case '-':kill[x]|=(1<<i);break;
        }
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i){
        scanf("%d%s%s",&a[i],s[1],s[2]);
        ready(i);
    }
    memset(dp,inf,sizeof(dp));
    dp[(1<<n)-1]=0;vis[(1<<n)-1]=true;
    t.push(((1<<n)-1));
    while(!t.empty()){
        int now=t.front();t.pop();
        vis[now]=false;
        for(int i=1;i<=m;++i){
            if((has[i]&now)!=has[i])continue;
            if((dont[i]&(~now))!=dont[i])continue;
            int to=(now&(~kill[i]))|bring[i];
            if(dp[to]<=dp[now]+a[i])continue;
            dp[to]=dp[now]+a[i];
            if(!vis[to]){
                t.push(to);vis[to]=true;
            }
        }
    }
    if(dp[0]==inf)printf("0\n");
    else printf("%d\n",dp[0]);
    return 0;
}

 

posted @ 2018-07-27 16:12  HolseLee  阅读(209)  评论(0编辑  收藏  举报