深度优先搜索专题(!!)

目录

A 家谱 

B 王子救公主

C 中国邮递员问题

D 中国象棋

E 等边三角形

F 方程的解数

G 引爆炸弹


A 家谱 

  • 2000ms
  • 131072K

家谱,又称族谱、宗谱等,是一种以表谱形式,记载一个家族的世系繁衍及重要人物事迹的书。皇帝的家谱称玉牒,如新朝玉牒、皇宋玉牒。它以记载父系家族世系、人物为中心,由正史中的帝王本纪及王侯列传、年表等演变而来。

家谱是一种特殊的文献,就其内容而言,是中国五千年文明史中具有平民特色的文献,记载的是同宗共祖血缘集团世系人物和事迹等方面情况的历史图籍。家谱属珍贵的人文资料,对于历史学、民俗学、人口学、社会学和经济学的深入研究,均有其不可替代的独特功能。

这一天蒜头君拿到了自己家的家谱,蒜头君便想知道,在自己家的家谱中,每位祖先有多少直系后代(直系后代包括他的孩子和他孩子的直系后代)。但是家族历史源远流长,家谱实在太庞大了,自己一个人完全数不过来。热心的你便自告奋勇帮蒜头君写一个程序,来统计每位祖先有多少直系后代。

输入格式

输入的第一行有一个整数 n(1≤n≤100000),表示家谱中的总人数。

接下来读入 n−1行,每行有两个整数 x(1≤x≤n), y(1≤y≤n),表示 x 是 y 的父母。

输出格式

输出 n 行,每行有一个整数,表示第 i个人有多少个直系后代。

要求使用「文件输入输出」的方式解题,输入文件为 family.in,输出文件为 family.out

样例输入

4
1 2
1 3
2 4

样例输出

3
1
0
0

【分析】

注意这道题是求每个节点的后代,就是所有的孩子节点的个数;

用vector把某个人的后代节点都压入;并且用vis数组记录是否有父节点;

从根节点开始深搜记录

【代码】

#include
using namespace std;

const int maxn=1e5+10;
vectorv[maxn];///存i的孩子
int ans[maxn];     ///存答案
int vis[maxn];     ///i是否有父母
int maxx=-1;

int dfs(int x)
{
	int cnt=0;
	for(int i=0;i

B 王子救公主

  • 1000ms
  • 131072K

一天,蒜头君梦见自己当上了王子,但是不幸的是,自己的公主被可恶的巫婆抓走了。于是蒜头君动用全国的力量得知,自己的公主被巫婆抓进一个迷宫里面。由于全国只有蒜头君自己可以翻越迷宫外的城墙,蒜头君便自己一人走上的拯救自己公主的路途。

碰巧的是巫婆出去了,迷宫也不大,蒜头君可以直接和公主对话,于是两个人便开始相互靠近。每一步移动只能朝着上下左右四个方向走一格,不能走进墙所在的位置。蒜头君救公主心切,一次必须沿着一个方向走两步(允许跨越迷宫中的墙);公主柔弱,一次只能走一步。问在这个迷宫中,蒜头君是否可以救出公主(蒜头君和公主相遇后,就能背着公主逃出迷宫了)。

输入格式

第一行输入两个整数 n(1≤n≤100), m(1≤m≤100) 表示迷宫的行和列。

然后有一个 n×m的地图,地图由'.''#''w''g'这四个部分组成。'.'表示可以通行的路,'#'表示迷宫的墙,'w'表示王子开始所在的位置,'g'表示公主开始所在的位置。

输出格式

输出王子是不可以救出自己的公主,如果能救出则输出"yes",否则输出"no"

输出时每行末尾的多余空格,不影响答案正确性

要求使用「文件输入输出」的方式解题,输入文件为 savior.in,输出文件为 savior.out

样例输入

1 8
w....#.g

样例输出

yes

【分析】

进行两次搜索,第一次对王子进行搜索标记王子能到达的所有点;然后再对公主进行搜索,如果公主能到达王子标记过的点,就说明王子是可以救出公主的;

定义两个三维数组,分别是深搜时用来记录该点是否到达以及记录王子和公主可以到达的路径用的;

【代码】

#include
using namespace std;

const int maxn=110;
char mp[maxn][maxn];
int vis[maxn][maxn][3];
int vis1[maxn][maxn][3];
int dir[4][2]={{0,1},{0,-1},{-1,0},{1,0}};

int n,m;
struct node{
    int x,y;
}w,g;

void dfs(int x,int y,int sex)
{
    if(mp[x][y]=='#')return;
    vis1[x][y][sex]=1;
    node tmp;tmp.x=x;tmp.y=y;
    for(int i=0;i<4;++i)
    {
        int xx=x+sex*dir[i][0];
        int yy=y+sex*dir[i][1];
        if(mp[x][y]=='#')return;
        if(xx<0 || xx>=n || yy<0 || yy>=m)continue;
        if(!vis[xx][yy][sex])
        {
	    vis[xx][yy][sex]=1;
            dfs(xx,yy,sex);
	}
    }
}
int main()
{
  //  freopen("savior.in","r",stdin);
  //  freopen("savior.out","w",stdout);

    scanf("%d%d",&n,&m);
    for(int i=0;i

C 中国邮递员问题

  • 1000ms
  • 131072K

一个邮递员从邮局出发,需要去 n−2n - 2n−2 个城市送信,送完信件以后回家。

邮局在城市 111,家在城市 nnn,任意两个城市之间都有道路,但是这些道路都是单向的,也就是说 aaa 到 bbb 和 bbb 到 aaa 的道路长度不一定是一样的。

他必须经过每个城市恰好一次,最后回到家里。

现在要求你计算他需要经过的路径总和的最小长度。

输入格式

第一行有一个整数 n(2≤n≤10)。

接下里输入一个 n×n 的邻接矩阵 G, G[i][j] 表示从 i 走到 j 的路径长度。

输出格式

输出一个整数,表示最小经过的路径总长度。

输出时每行末尾的多余空格,不影响答案正确性

要求使用「文件输入输出」的方式解题,输入文件为 mail.in,输出文件为 mail.out

样例输入

4
0 1 1 1
1 0 2 1
5 5 0 6
1 1 3 0

样例输出

7

【分析】

【代码】

#include
using namespace std;

const int maxn=20;
const int inf=0x3f3f3f3f;
int G[maxn][maxn];
int vis[maxn];///标记第i个城市是否走过
int n,ans;

void dfs(int x,int dis,int dep)///当前城市x,总共走过的路径长度dis,已经走过dep个城市
{
    if(dep==n)
    {
        if(x==n)ans=min(ans,dis);
        return;
    }
    for(int i=1;i<=n;++i)
    {
        if(!vis[i] && G[x][i])
        {
            vis[i]=1;
            dfs(i,dis+G[x][i],dep+1);
            vis[i]=0;
        }
    }
}
int main()
{
   // freopen("mail.in","r",stdin);
   // freopen("mail.out","w",stdout);

    scanf("%d",&n);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
        scanf("%d",&G[i][j]);
    vis[1]=1;
    ans=inf;
    dfs(1,0,1);
    printf("%d\n",ans);
    return 0;
}

D 中国象棋

  • 1000ms
  • 131072K

中国象棋是起源于中国的一种棋类游戏,属于二人对抗性游戏的一种,在中国有着悠久的历史。由于用具简单、趣味性强,成为流行极为广泛的棋艺活动。

这天,蒜头君迷上了中国象棋,在和一个大师的巅峰对决中处于下风。他知道自己再走三步,大师就会赢下这一局,于是蒜头君想背水一战。

他想知道这个马走三步之内可以到达的位置,是否有好的对策可以给大师致命一击。现在蒜头君的脑子已经不足以想出马走三步之内能到达的所有位置了,于是他找到作为他好朋友的你来帮忙解决这个问题。

马走动的方法是一直一斜,即先横着或竖着走一格,然后再斜着走一个对角线,俗称“马走日”。马走一次可以达到四周的八个点,故有“八面威风”之说。如果在要去的方向有别的棋子挡住,马就无法走过去,俗称“蹩马腿”。为了简化题目,这里就没有蹩马腿这回事了。

输入格式

第一行输入两个整数 n(1≤n≤100), m(1≤m≤100) 代表棋盘行数和列数。

第二行输入两个整数 x(1≤x≤n),y(1≤y≤m)  代表马的初始位置。

输出格式

输出整个棋盘,'.'代表棋盘上可以落子的点,'#'这个代表马走三步能到达的点。

输出时每行末尾的多余空格,不影响答案正确性

要求使用「文件输入输出」的方式解题,输入文件为 chess.in,输出文件为 chess.out

样例输入

10 9
10 1

样例输出

.........
.........
.........
.#.#.....
#.#.#....
####.#...
#####.#..
##.###...
#.###.#..
######...

【代码】

#include
using namespace std;

const int maxn=110;
char mp[maxn][maxn];
int vis[maxn][maxn];
int dir[8][2]={{-2,1},{-2,-1},{2,1},{2,-1},{-1,2},{-1,-2},{1,2},{1,-2}};
int n,m;
int x,y;

void dfs(int x,int y,int dep)
{
    if(dep==4)return;
    for(int i=0;i<8;++i)
    {
        int xx=x+dir[i][0];
        int yy=y+dir[i][1];
        if(xx<0 || xx>=n || yy<0 || yy>=m)continue;
        if(vis[xx][yy])continue;
        if(!vis[xx][yy])
        {
            vis[xx][yy]=1;
           // cout<

E 等边三角形

  • 2000ms
  • 131072K

蒜头君手上有一些小木棍,它们长短不一,蒜头君想用这些木棍拼出一个等边三角形,并且每根木棍都要用到。 例如,蒜头君手上有长度为 111,222,333,333 的4根木棍,他可以让长度为111,222 的木棍组成一条边,另外 222 跟分别组成 222 条边,拼成一个边长为 333 的等边三角形。蒜头君希望你提前告诉他能不能拼出来,免得白费功夫。

输入格式

首先输入一个整数 n(3≤n≤20),表示木棍数量,接下来输入 n 根木棍的长度 pi(1≤pi≤10000)。

输出格式

如果蒜头君能拼出等边三角形,输出"yes",否则输出"no"

输出时每行末尾的多余空格,不影响答案正确性

要求使用「文件输入输出」的方式解题,输入文件为 triangle.in,输出文件为 triangle.out

样例输入1

5
1 2 3 4 5

样例输出1

yes

样例输入2

4
1 1 1 1

样例输出2

no

【分析】注意是每条边都要用到的!

【代码】

#include
using namespace std;

const int maxn=30;
int a[maxn];
int n,f,sum;

void dfs(int x,int y,int z,int id)
{
    if(f)return;
    if(x>sum || y>sum || z>sum)return;
    if(x==sum && y==sum && z==sum)
    {
        if(id==n)f=1;
        return;
    }
    /*不可以这样合起来写,这样的话只要有一个满足sum就返回了,但是sum 0 0这种情况是可以继续搜的,但是这样写的话就直接return掉了
    if(x>=sum || y>=sum || z>=sum || id==n)
    {
        if(id==n && x==sum && y==sum && z==sum)f=1;
        return;
    }
    */
    dfs(x+a[id],y,z,id+1);
    dfs(x,y+a[id],z,id+1);
    dfs(x,y,z+a[id],id+1);
}
int main()
{
 //   freopen("triangle.in","r",stdin);
 //   freopen("triangle.out","w",stdout);
    scanf("%d",&n);
    for(int i=0;i

F 方程的解数

  • 1000ms
  • 131072K

 

假设未知数 1≤xi≤M,i=1…n  你能帮蒜头君算出这个方程的整数解个数吗?

输入格式

第一行输入一个整数 n(1≤n≤4)

第二行输入一个整数 M(1≤M≤150)。

第 3 行到第 n+2 行,每行输入两个整数,分别表示 ki(∣ki∣≤20) 和 pi(1≤pi≤4)。两个整数之间用一个空格隔开。

输出格式

输出一行,输出一个整数,表示方程的整数解的个数。

输出时每行末尾的多余空格,不影响答案正确性

要求使用「文件输入输出」的方式解题,输入文件为 equation.in,输出文件为 equation.out

样例输入

3
100
1 2
-1 2
1 2

样例输出

104

【分析】

注意x不是一个,是多个...

枚举每个x的所有可能的值,然后看结果是否为0

计算幂次的时候要预处理,不然会T的;还有不同的IDE的pow函数是不一样的,所以最好自己定义一个;

【代码】

#include
using namespace std;

int n, m, cnt;
int k[10], p[10];
int ppow[5][150];

int pow(int x,int y)
{
    int sum = 1;
    for(int i = 0; i < y; ++i)
        sum *= x;
    return sum;
}
void dfs(int id, int sum)//枚举到第id个x,当前的和为sum
{
    if(id == n+1)
    {
        if(sum == 0)
             cnt++;
        return;
    }
    for(int i = 1; i <= m; ++ i)
        dfs(id+1, sum + ppow[id][i]);
}
int main()
{
    freopen("equation.in","r",stdin);
    freopen("equation.out","w",stdout);
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d%d", &k[i], &p[i]);
        for(int j=1;j<=m;++j)///预处理
            ppow[i][j]=k[i]*pow(j,p[i]);
    }
    dfs(1, 0);
    printf("%d\n", cnt);
    return 0;
}

G 引爆炸弹

  • 1000ms
  • 131072K

在一个 n×m 的方格地图上,某些方格上放置着炸弹。手动引爆一个炸弹以后,炸弹会把炸弹所在的行和列上的所有炸弹引爆,被引爆的炸弹又能引爆其他炸弹,这样连锁下去。

深度优先搜索专题(!!)_第1张图片

现在为了引爆地图上的所有炸弹,需要手动引爆其中一些炸弹,为了把危险程度降到最低,请算出最少手动引爆多少个炸弹可以把地图上的所有炸弹引爆。

输入格式

第一行输两个整数 n,m,用空格隔开。

接下来 n 行,每行输入一个长度为 m 的字符串,表示地图信息。0表示没有炸弹,1表示炸弹。

数据约定:

对于 40%的数据:1≤n,m≤100;

对于 100 的数据:1≤n,m≤500;

输出格式

输出一个整数,表示最少需要手动引爆的炸弹数。

输出时每行末尾的多余空格,不影响答案正确性

要求使用「文件输入输出」的方式解题,输入文件为 boom.in,输出文件为 boom.out

样例输入

5 5
00010
00010
01001
10001
01000

样例输出

2

【分析】连锁引爆,深搜

【代码】

#include
using namespace std;

const int maxn=510;
char mp[maxn][maxn];
int n,m;

void dfs(int x, int y)
{
    if(mp[x][y]=='1')mp[x][y]='0';
    for(int i = 0; i < m; ++i)
        if(mp[x][i] == '1')dfs(x, i);
    for(int i = 0; i < n; ++i)
        if(mp[i][y] == '1')dfs(i, y);
}
int main()
{
   // freopen("boom.in","r",stdin);
   // freopen("boom.out","w",stdout);
    scanf("%d%d",&n, &m);
    for(int i = 0; i < n; ++i)
        scanf("%s", mp[i]);
    int cnt=0;
    for(int i = 0; i < n; ++i)
    {
        for(int j =0; j < m; ++j)
            if(mp[i][j] == '1')
            {
                cnt++;
                dfs(i, j);
            }
    }
    printf("%d\n", cnt);
    return 0;
}

 

你可能感兴趣的:(DFS/BFS,---)