Bootstrap

专题训练之BFS/DFS 二分

一、DFS是什么?

dfs是指深度优先搜索。从我的理解来说,就是从一个节点出发,一直往下走,走到无法继续的时候,再返回上一层,继续尝试;直到走完所有情况,或者找到满足条件的。

二、BFS是什么?

bsf是指宽度优先搜索。从我的理解来说,就是从一个节点出发,一步步地走从该节点出发,实现所有的可能性。

三、BFS和DFS的区别

在以前学信息学竞赛的时候,我们老师曾这么这么解释:bfs就是从一个点出发,一直走到底,竖着一列列搜索;dfs就是从一个点出发,走所有可能的地方,横着一层层搜索。

四、需要注意的点

搜索完当前点,回溯的时候,要注意还原原来的状态。例如在取书问题中,我们可以用一个数组标记哪些书已经取过,在回溯的时候,记得要将该书重新标记回未取过。

基本模板:

int check(参数)
{
    if(满足条件)
        return 1;
    return 0;
}
 
void dfs(int step)
{
        判断边界
        {
            相应操作
        }
        尝试每一种可能
        {
               满足check条件
               标记
               继续下一步dfs(step+1)
               恢复初始状态(回溯的时候要用到)
        }
}   

五、二分是什么?

二分最经典的案例,便是猜数游戏,每次猜当前范围的中值,是猜到目标值的最优方法。二分思想便是设置边界,将中值与目标想相比较;然后转换边界,以中值为界限,转换为左边界或右边界。

下面这个是一个师兄推荐的,也是我最喜欢的一个模板

int l = 下界 - 1  r = 上界 + 1
while (l+1 <r) {
  int mid = (l+r) >> 1;
  if (c(mid)) l = mid; else r = mid;
}
//l 为满足 c(x) 的最大值, r为不满足c(x)的最小值

六、专题题解

1. Lake Counting

题目链接

题意:给出n*m方格,求有多少个被“.”包围的“w”区域

思路:经典dfs题,搜索一下。

#include <iostream>
#include <string>
using namespace std;
string s[110];
int a[8][2]={{0,1},{0,-1},{1,0},{1,-1},{1,1},{-1,0},{-1,1},{-1,-1}};
int n,m;
bool check(int x,int y)
{
    if (s[x][y]=='W') return 1;
     else return 0;
}

void dfs(int x,int y)
{
    if (check(x,y))
    {
        s[x][y]='.';
        for (int i=0;i<8;i++)
        {
            int x1,y1;
            x1=x+a[i][0];
            y1=y+a[i][1];
            if ((x1>=0)&&(x1<n)&&(y1>=0)&&(y1<m))
                dfs(x1,y1);
        }
        return;

    }else
    return;
}
int main()
{
    cin>>n>>m;
    int i,j,ans=0;
    for (i=0; i<n; i++)
    {
        cin>>s[i];
    }
    for (i=0; i<n; i++)
        for (j=0; j<m; j++)
        {
            if (check(i,j))
            {
                dfs(i,j);
                ans++;
            }
        }
    cout <<ans<<endl;
    return 0;
}

2.Red and Black

题目链接

题意:给出n*m方格,求从“@”出发,不碰及“#”边界,可以走到的“.”有多少个?

思路:与上题相似,先找到“@”点,从该点出发深搜。

#include <iostream>
int n,m,ans;
using namespace std;
char s[30][30];
int ss[30][30];
int a[4][2]= {{0,1},{0,-1},{1,0},{-1,0}};

bool check(int x,int y)
{
    if (s[x][y]!='#'&&ss[x][y]==0&&x>=0&&x<n&&y>=0&&y<m)
        return 1;
    else
        return 0;
}
void dfs(int x,int y)
{
    if (check(x,y))
    {
        ans++;
        ss[x][y]=1;
        for (int i=0; i<4; i++)
        {
            int x1,y1;
            x1=x+a[i][0];
            y1=y+a[i][1];
            dfs(x1,y1);
        }
    }
    else
        return;
}


int main()
{
    while (1)
    {
        cin>>m>>n;
        if (n==0&&m==0)
            break;
        int i,j,x,y;
        ans=0;
        for (i=0; i<n; i++)
        {
            for (j=0; j<m; j++)
            {
                cin>>s[i][j];
                ss[i][j]=0;
                if (s[i][j]=='@')
                {
                    x=i;
                    y=j;
                }
            }
        }
        dfs(x,y);
        cout<<ans<<endl;
    }
    return 0;
}

3. Accepted Necklace

题意:实际为01背包

思路:可以用dp,这里锻炼一下dfs。dfs过程中记录一下所用石子数量,所用石子价值,所用石子重量,再判断一下结束边界即可。

#include <iostream>
#include <string.h>
using namespace std;

struct shi
{
    int a;
    int b;
} shi[110];

int n,ans,k,wmax;
int v[110];

void dfs(int x,int w,int y,int dex)
{
    if (w==wmax||x==k)
    {
        if (ans<y)
            ans=y;
    }
    else if (w<wmax&&x<k)
    {
        for (int i=dex; i<n; i++)
        {
            if  (v[i]==0&&(x+1)<=k&&(w+shi[i].b)<=wmax)
            {
                v[i]=1;
                dfs(x+1,w+shi[i].b,y+shi[i].a,i+1);
                v[i]=0;
            }
        }
    }

}
int main()
{
    int t;
    cin>>t;
    while (t--)
    {
        memset(v,0,sizeof(v));
        cin>>n>>k;
        int i;
        ans=0;
        for (i=0; i<n; i++)
            cin>>shi[i].a>>shi[i].b;
        cin>>wmax;
        dfs(0,0,0,0);
        cout<<ans<<endl;
    }
    return 0;
}

;