vijos1026 BFS与位运算

Posted zarax

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vijos1026 BFS与位运算相关的知识,希望对你有一定的参考价值。

题目描述

羽毛笔和im是抽签到同一个考场的,她们突然闻到一阵刺鼻的化学试剂的气味。

机灵鼠:(头都不抬)你们是考生么?还在门口磨蹭什么?快进来帮我忙!!……怎么还不进来?你们拖赛,拖赛,把你们的青春都拖掉赛……
im:开…开策了>_<
羽毛笔:哎呀~~机灵鼠大人要我们帮什么忙?^^
机灵鼠:你们看这里的这些药,都是我研制的对付各种症状的解药。可是我一个不小心,每种药都小小地配错了一点原料,所以这些药都有可能在治愈某些病症的同时又使人患上某些别的病症……(im:那…那是解药还是毒药啊?!)……经过我天才的努力(背景:我是天才!!),终于弄清了每种药的具体性能(路人甲:那是你自己配的吗?-_-),我会把每种药能治的病症和能使人患上的病症列一张清单给你们,然后你们要根据这张清单找出能治愈所有病症的最少药剂组合……顺便说一声,病症的数目不超过10种(小呆:偶是好人吧^^),我的药是用不完的,就是说每种药剂都可以被重复使用。给你们的单子里第一行是病症的总数n,第二行是药剂的种类m(0<m<=100),以下有m行,每行有n个数字用空格隔开,文件的第i+2行的n个数字中,如果第j个数为1,就表示第i种药可以治愈病症j(如果患有这种病的话则治愈,没有这种病则无影响),如果为0表示无影响,如果为-1表示反而能使人得上这种病(无病患上,有病无影响)。我制的药任何两种性能都不同。你们只要给我用的最少的药剂数就可以了。给你们个样例:

 

输入:

3
2
1 0 1
-1 1 0

输出:

2

如果没有解则输出“The patient will be dead.”

 

这是一道搜索题(我其实并没想明白为什么没人用DFS)

与图论不同,没有显式的图的问题很大的一个难点就是:用什么表示状态?

这里给出了一个方法:用二进制位来表示状态

但前提是,需要表示的状态量不是很多,因为每增加一个状态量,相应的数字就会以2n形式增长

 

那么水到渠成的,二进制的十个位分别表示这个患者的10个病是否被治好

BFS基本思路:每次取出一个状态,用所有药品试一遍,如果更新出了前面更新过的状态(即d[i]!=MAXN)则比较所用步数,当前用的更少就更新,否则不变。

BFS需要队列,这里直接使用c++的queue

 

初始状态:这个病人10个病都有,那么设初始状态位s,则:

s=(1<<n)-1

s的第i位(i取0~n-1)分别表示是否患上该病

用一个d[]数组表示:d[i]代表到达状态为i的最少用药次数,即最少步数,初始化为一个很大的数

 

用a[]数组表示:a[i]的第j位表示如果为1,表示第i号药品能够治愈第j号病,为0则不能

用b[]数组表示:b[i]的第j位表示如果为1,表示第i号药品能够让人患第j号病,为0则不能

 

用in[]数组表示:如果in[i]=1则第i号药品已经在队列中

为什么要用in数组?-----一个很蠢的问题但我想了半天...

事实上,这个问题等价于:为什么一个状态入队之后,出队前它可不可以再次入队?

:这要从BFS说起,当前问题比较特殊,每个状态到下一个状态的代价,即每个节点到下一个节点的边权都是1,所以很容易证明,如果一个状态入队了,那么这个状态入队时的解一定是最优解

  因为BFS是分层的,上层的代价一定比下层要小。但真正在图论中就不同了,因为有边权问题,所以不一定先来的代价就小。其实这道题 不用in[]/不出队也行

 

如何更新状态?当然是位运算

int update(int state,int num){
    state&=(~a[num]);
    state|=b[num];
    return state;
}

这个函数接受两个参数:状态码和使用的药品,如果没患病(state相应位是0)则没效果,如果患病且能治愈(state=1且a=1)也变为0,其余不变。b是相似的原理

 

之后就是喜闻乐见的SPFA啦!

void SPFA(){
    queue<int > q;
    q.push(s); d[s]=0;in[s]=1;
    while(!q.empty()){
        int u=q.front();q.pop(); //u为当前状态 
        for(int i=1;i<=m;i++){
            int v=update(u,i);//v是更新后的状态 
            if(d[v]>d[u]+1){
                d[v]=d[u]+1;
                if(!in[v]){
                    q.push(v);
                    in[v]=1;
                }
            }
        }
    }
    if(d[0]==MAXN)printf("The patient will be dead.");
    else printf("%d",d[0]);
}

 

完整代码:

#include <iostream>
#include <algorithm>
#include <queue>
#include <cstdio>
using namespace std;
#define MAXN 2147483640
const int MAX=(1<<20);//最大步数 
int a[101],b[101],d[(1<<10)+1],in[(1<<10)+1];
int n,m,s;
void init(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        for(int j=0;j<n;j++)
        {
            int x;scanf("%d",&x);
            if(x==1)a[i]|=(1<<j);
            else if(x==-1)b[i]|=(1<<j);
        }
    }
    for(int i=0;i<=(1<<n);i++)d[i]=MAXN;
    s=(1<<n)-1;
}
int update(int state,int num){
    state&=(~a[num]);
    state|=b[num];
    return state;
}
void SPFA(){
    queue<int > q;
    q.push(s); d[s]=0;in[s]=1;
    while(!q.empty()){
        int u=q.front();q.pop(); //u为当前状态 
        for(int i=1;i<=m;i++){
            int v=update(u,i);//v是更新后的状态 
            if(d[v]>d[u]+1){
                d[v]=d[u]+1;
                if(!in[v]){
                    q.push(v);
                    in[v]=1;
                }
            }
        }
    }
    if(d[0]==MAXN)printf("The patient will be dead.");
    else printf("%d",d[0]);
}
int main(){
    init();
    SPFA();
    return 0;
}

 

 

最后的最后,我注意到了对于位运算的暗示:有病就患上,没有就无影响;无病就患上,有病就无影响

我想这应该是一种典型的性质,如果是就改,不是就不变,正像&1,是1就触发,0就不变

以上是关于vijos1026 BFS与位运算的主要内容,如果未能解决你的问题,请参考以下文章

hdu 1026 Ignatius and the Princess I(BFS+优先队列)

HDU1026--Ignatius and the Princess I(BFS记录路径)

hdu 1026(Ignatius and the Princess I)BFS

HDU 1026 Ignatius and the Princess I(BFS+记录路径)

HDU 1026 Ignatius and the Princess I(BFS+路径输出)

HDU_1026_Ignatius and the Princess I_BFS(保存路径)