搜索练习题【题解】

  • VIJOS-P1026 毒药解药

    • Description
    • Sample Input
    • Sample Output
    • HINT
    • Source
    • Solution
  • POJ3321Apple Tree
    • Description
    • Input
    • Output
      • Sample Input
      • Sample Output
    • Source
    • Solution

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]=1;
                    Q.push(tmp);
                }
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&a[i][j]);
        }
    }
    st=0,ed=(1<<n)-1;
    spfa(ed);
    if(dep[0]==0x3f3f3f3f)
    {
        puts("The patient will be dead.") ;
    }
    else printf("%d\n",dep[0]);
    return 0;
}

解释与说明

用二进制表示,假设一共有N种病,令111…1(N个1)表示所有病都患,0就是没有病,则st=0,ed=(1 < < N )-1就是N个1 的十进制表示。

然后BFS时只要判断st能不能转移到ed就可以。

PS:位运算大法Newbility

方法二

Hash表判重,原理同上。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct node
{
    int dep;
    int b[15];
}list[1<<10+1];
int n,m,st,ed;
int mp[150][15];
bool hash[1<<10+1];
void bfs()
{
    memset(hash,false,sizeof(hash));
    st=0,ed=(1<<n)-1;
    int head=1,tail=1;
    while(head<=tail)
    {
        for(int i=1;i<=m;i++)
        {
            tail++;
            st=0;
            for(int j=1;j<=n;j++)
            {
                if(mp[i][j]==1) list[tail].b[j]=1;
                if(mp[i][j]==0) list[tail].b[j]=list[head].b[j];
                if(mp[i][j]==-1)list[tail].b[j]=0;
                if(list[tail].b[j]==1)  st+=(1<<(j-1));
            }
            if(!hash[st])
            {
                hash[st]=true;
                list[tail].dep=list[head].dep+1;
                if(st==ed)
                {
                    printf("%d\n",list[tail].dep);
                    exit(0);
                }
            }
            else tail--;
        }
        head++;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&mp[i][j]);
        }
    }
    bfs();
    printf("The patient will be dead.\n");
    return 0;
}

POJ3321【Apple Tree】

Time Limit: 2000MS Memory Limit: 65536K

Total Submissions: 21614 Accepted: 6563

Description

There is an apple tree outside of kaka’s house. Every autumn, a lot of apples will grow in the tree. Kaka likes apple very much, so he has been carefully nurturing the big apple tree.

The tree has N forks which are connected by branches. Kaka numbers the forks by 1 to N and the root is always numbered by 1. Apples will grow on the forks and two apple won’t grow on the same fork. kaka wants to know how many apples are there in a sub-tree, for his study of the produce ability of the apple tree.

The trouble is that a new apple may grow on an empty fork some time and kaka may pick an apple from the tree for his dessert. Can you help kaka?

Input

The first line contains an integer N (N ≤ 100,000) , which is the number of the forks in the tree.

The following N - 1 lines each contain two integers u and v, which means fork u and fork v are connected by a branch.

The next line contains an integer M (M ≤ 100,000).

The following M lines each contain a message which is either

“C x” which means the existence of the apple on fork x has been changed. i.e. if there is an apple on the fork, then Kaka pick it; otherwise a new apple has grown on the empty fork.

or

“Q x” which means an inquiry for the number of apples in the sub-tree above the fork x, including the apple (if exists) on the fork x

Note the tree is full of apples at the beginning

Output

For every inquiry, output the correspond answer per line.

Sample Input

3

1 2

1 3

3

Q 1

C 2

Q 1

Sample Output

3

2

Source

POJ Monthly–2007.08.05, Huang, Jinsong



DFS序【树状数组维护】

Solution:

以下图为例:

DFS序:

对图进行深度优先遍历时,按访问顶点的先后次序得到的顶点序列称为该图的深度优先遍历序列,或简称为DFS序列

所以对于上图来说,它的DFS序可以是 1 3 6 2 4 5也可以是1 2 4 5 3 6

对于一个点来说,它会有两个时间戳,一个进栈的时间戳一个出栈的时间戳,用in[i],out[i]表示

i in[i] out[i]
1 1 6
2 4 6
3 2 3
4 6 6
5 5 5
6 3 3

然后我们就可以发现,在DFS序中in[i]~out[i]的数字就是i的子树编号。

所以我们就需要处理出来in[],out[]数组然后用树状数组维护增减以及求和就好了。

Code:

#include <stdio.h>
#include <string.h>
#define MAXN 150000
struct node
{
    int from;
    int to;
    int next;
}edge[MAXN];
int n,m,cnt,tot,tmp;
int head[MAXN],c[MAXN],order[MAXN],in[MAXN],out[MAXN];
bool judge[MAXN];
void init()
{
    memset(head,-1,sizeof(head));
    cnt=1;
}
void addedge(int from,int to)
{
    edge[cnt].from=from;
    edge[cnt].to=to;
    edge[cnt].next=head[from];
    head[from]=cnt++;
}
int lowbit(int x)
{
    return x&(-x);
}
int get_sum(int x)
{
    int sum=0;
    while(x)
    {
        sum+=c[x];
        x-=lowbit(x);
    }
    return sum;
}
void update(int x,int val)
{
    while(x<=MAXN)
    {
        c[x]+=val;
        x+=lowbit(x);
    }
}
void dfs(int u)
{
    in[u]=++tot;    //进入 时间戳
    order[tot]=u;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        dfs(v);
    }
    out[u]=tot;     //退出 时间戳
}
int main()
{
    init();
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        addedge(a,b);
    }
    dfs(1);
    for(int i=1;i<=n;i++)
    {
        update(i,1);
        judge[i]=true;
    }
    /*
    for(int i=1;i<=tot;i++)
    {
        printf("%d:",i);
        for(int j=in[i];j<=out[i];j++)
        {
            printf("%d ",order[j]);
        }
        printf("\n");
    }
    */
    scanf("%d",&m);
    char s[3];
    for(int i=1;i<=m;i++)
    {
        scanf("%s %d",s,&tmp);
        if(s[0]==‘Q‘)
        {
            printf("%d\n",get_sum(out[tmp])-get_sum(in[tmp]-1));
        }
        else
        {
            if(judge[tmp]==false)
            {
                update(in[tmp],1);
                judge[tmp]=true;
            }
            else
            {
                update(in[tmp],-1);
                judge[tmp]=false;
            }
        }
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-13 07:48:42

搜索练习题【题解】的相关文章

搜索练习题1215:迷宫

题目链接:http://ybt.ssoier.cn:8088/problem_show.php?pid=1215 [题目描述] 一天Extense在森林里探险的时候不小心走入了一个迷宫,迷宫可以看成是由n * n的格点组成,每个格点只有2种状态,.和#,前者表示可以通行后者表示不能通行.同时当Extense处在某个格点时,他只能移动到东南西北(或者说上下左右)四个方向之一的相邻格点上,Extense想要从点A走到点B,问在不走出迷宫的情况下能不能办到.如果起点或者终点有一个不能通行(为#),则看

挑战程序2.1.4 穷竭搜索&gt;&gt;深度优先搜索 练习题 POJ1979黑与红

http://poj.org/problem?id=1979 Description There is a rectangular room, covered with square tiles. Each tile is colored either red or black. A man is standing on a black tile. From a tile, he can move to one of four adjacent tiles. But he can't move

单调栈练习题题解

单调栈 单调栈顾名思义就是让栈中的元素是单调的,要么递增,要么递减.同样它也满足栈的性质,先进后出. 单调递增栈,则从栈顶到栈底的元素是严格递增的 单调递减栈,则从栈顶到栈底的元素是严格递减的 练习题 单调栈 练习题 POJ3250 POJ2796 BZOJ1113 HDU1506 POJ2559 JDFZ2997 POJ3250 POJ3250传送门 对于每一个牛来说,能看到的数目为向右数身高比它小的个数,累加就是答案. 所以可以倒着维护一个单调递增的栈,记录i之前的弹栈数目,累加. (正着也

算法期末考试练习题

一.选择题 1.算法分析中,记号O表示(B),记号?标售(A),记号Θ表示(D) A 渐进下界 B 渐进上界 C 非紧上界 D 紧渐进界 E 非紧下界 2.以下关于渐进记号的性质是正确的有:(A) A  f(n) =Θ(g(n)),g(n) =Θ(h(n)) ⇒f(n) =Θ(h(n)) B  f(n) =O(g(n)),g(n) =O(h(n)) ⇒h(n) =O(f(n)) C  O(f(n))+O(g(n)) = O(min{f(n),g(n)}) D  f(n) = O(g(n)) ⇔g

寒假练习题解 第四周 2.8-2.14

每日一练 搜索专题 题目选自[kuangbin带你飞]专题一 简单搜索 简要题解(来源网络) 网上详细题解很多,自行查找.

FOJ月赛 2014年11月 题解

Problem A: Yellowstar的第一道题 写个暴力程序会发现若n*r*c 是偶数,则是必败态,输出0.000000 否则对于3*3*3 赢的位置有: 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1为必胜点.也就是说左上角是1,这样扩散出去. 答案就是所有1位置的概率和. 题解:点击打开链接 Problem B easy problem 因为k很小 公式: ((dep[y]-dep[x])%k+1)*val 当确定depx

题解 P2327 [SCOI2005]扫雷

题解 P2327 [SCOI2005]扫雷 题目链接 普及组水题非常适合新手练搜索 看题解里各路神仙都用各种简单的方法,我来讲讲暴力搜索做法.... 对于我这样的萌新我觉得这样的做法更容易让你理解搜索 首先搜索要定义状态,我们定义dfs(x, p)是当第x位是p,就有了状态 然后我们讨论第x + 1位的情况只有两种情况,然后就有了转移 其次当x是n时,如果成立,则++ans,于是有了边界条件 最后你把这段代码复制粘贴得了100分,发现原来这有点像动态规划 #include <iostream>

COJN 0487 800301红与黑

800301红与黑 难度级别:B: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 有一间长方形的房子,地上铺了红色.黑色两种颜色的正方形瓷砖.你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动.请写一个程序,计算你总共能够到达多少块黑色的瓷砖. 输入 包括多个数据集合.每个数据集合的第一行是两个整数W和H,分别表示x方向和y方向瓷砖的数量.W和H都不超过20.在接下来的H行中,每行包括W个字符.每个字符表示一块瓷砖的颜色,规则如下1)‘

五大常用算法

http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741370.html 分治算法 一.基本概念 在计算机科学中,分治法是一种很重要的算法.字面上的解释是"分而治之",就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题--直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并.这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)-- 任何一个可以用计