VIJOS-P1026 毒药?解药?
Description
羽毛笔和im是抽签到同一个考场的,她们突然闻到一阵刺鼻的化学试剂的气味。
机灵鼠:(头都不抬)你们是考生么?还在门口磨蹭什么?快进来帮我忙!!……怎么还不进来?你们拖赛,拖赛,把你们的青春都拖掉赛……
im:开…开策了> _<
羽毛笔:哎呀~~机灵鼠大人要我们帮什么忙?^^
机灵鼠:你们看这里的这些药,都是我研制的对付各种症状的解药。可是我一个不小心,每种药都小小地配错了一点原料,所以这些药都有可能在治愈某些病症的同时又使人患上某些别的病症……(im:那…那是解药还是毒药啊?!)……经过我天才的努力(背景:我是天才!!),终于弄清了每种药的具体性能(路人甲:那是你自己配的吗?-_-),我会把每种药能治的病症和能使人患上的病症列一张清单给你们,然后你们要根据这张清单找出能治愈所有病症的最少药剂组合……顺便说一声,病症的数目不超过10种(小呆:偶是好人吧^^),我的药是用不完的,就是说每种药剂都可以被重复使用。给你们的单子里第一行是病症的总数n,第二行是药剂的种类m(0< m< =100),以下有m行,每行有n个数字用空格隔开,文件的第i+2行的n个数字中,如果第j个数为1,就表示第i种药可以治愈病症j(如果患有这种病的话则治愈,没有这种病则无影响),如果为0表示无影响,如果为-1表示反而能使人得上这种病(无病患上,有病无影响)。我制的药任何两种性能都不同。你们只要给我用的最少的药剂数就可以了。给你们个样例:
Sample Input
3
2
1 0 1
-1 1 0
Sample Output
2
HINT
其实还有可能用尽了所有的药也不能将所有病治愈(真是不好意思嗬^^bb),那样的话你们只要写上“The patient will be dead.”就可以了。 im:做不出来啊哇啊啊啊(暴走中) 羽毛笔:哎呀~~im……来来吃药了。^^
Source
VIJOS
状态压缩BFS
Solution:
很显然我们首先会得到一个N*M的表格,表示第i种要对于第j种症状的作用效果。当N等于3的情况,可以得到如下的几种状态:
√ | × |
---|---|
1 | 2 3 |
2 | 1 3 |
3 | 1 2 |
1 2 | 3 |
1 3 | 2 |
2 3 | 1 |
1 2 3 | ∅ |
∅ | 1 2 3 |
一共会有8中状态,同理 可以得到更普遍的结论:N种病共有 2N 种状态;
这样我们就会有两种方法:
方法一:
若状态i可以转移成状态j,则i->j 可以建一条有向边。然后跑最短路就好。
但是说着简单,其实还是有很多注意事项的。先上代码:
Code:
#include <stdio.h>
#include <string.h>
#include <queue>
#define MAXN 1<<(10)+1
using namespace std;
int n,m,st,ed;
int a[150][15],dep[MAXN],visit[MAXN];
void spfa(int src)
{
memset(dep,0x3f,sizeof(dep));
dep[src]=0;
queue<int>Q;
Q.push(src);
while(!Q.empty())
{
int u=Q.front();Q.pop();
for(int i=1;i<=m;i++)
{
int tmp=u;
for(int j=1;j<=n;j++)
{
if((tmp&(1<<(j-1)))&&a[i][j]==1) // 二进制情况下,第j位为 1 : - > 有病
//并且当前药物可以治疗
{
tmp-=1<<(j-1); //让 第 j 位变成 0
}
if((!(tmp&(1<<(j-1))))&&a[i][j]==-1)
{
tmp+=1<<(j-1);
}
}
if(dep[tmp]>dep[u]+1) //当前情况达到 tmp 的步数 能 更新
{
dep[tmp]=dep[u]+1;
if(!visit[tmp])
{
visit[tmp]=