DFS题库+详解

DFS

1.走出迷宫-NC14572

小明现在在玩一个游戏,游戏来到了教学关卡,迷宫是一个N*M的矩阵。 小明的起点在地图中用“S”来表示,终点用“E”来表示,障碍物用“#”来表示,空地用“.”来表示。 障碍物不能通过。小明如果现在在点(x,y)处,那么下一步只能走到相邻的四个格子中的某一个:(x+1,y),(x-1,y),(x,y+1),(x,y-1); 小明想要知道,现在他能否从起点走到终点。

输入描述:
本题包含多组数据。每组数据先输入两个数字N,M接下来N行,每行M个字符,表示地图的状态。数据范围:2<=N,M<=500保证有一个起点S,同时保证有一个终点E.
输出描述:
每组数据输出一行,如果小明能够从起点走到终点,那么输出Yes,否则输出No

输入:
3 3
S…
…E

3 3
S##

##E
输出:
Yes
No

#include<bits/stdc++.h>
#include<time.h>
#include<iostream>
#include<ostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<string>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
#define maxn 10000000
typedef long long ll;
const int INF=0x7fffffff;//2147483647
const int SUP=0x80000000;//-2147483648
#define loop(a,b,i) for(int i=a;i<b;i++)
#define loopx(a,b,i) for(int i=a;i<=b;i++)
#define lson (p<<1)
#define rson ((p<<1)|1)
#define mid ((l+r)>>1)
#define MAX 100005
char dfs[505][505];
int dir[4][2]={1,0,-1,0,0,-1,0,1};
int n,m,flag;
void DFS(int x,int y)
{
    for(int i=0;i<4;i++)
    {
        int X=x+dir[i][0],Y=y+dir[i][1];
        if(X>=0&&X<n&&Y>=0&&Y<m&&dfs[X][Y]!='#'&&dfs[X][Y]!='1')
        {
            if(dfs[X][Y]=='E')flag=1;
            dfs[X][Y]='1';
            DFS(X,Y);
        }
    }
}
int main()
{
    while(cin>>n>>m)
    {
        int x,y;
        memset(dfs,'0',sizeof(dfs));
        flag=0;
        for (int i=0;i<n;i++)
            for (int j=0;j<m;j++)
            {
                cin>>dfs[i][j];
                if (dfs[i][j]=='S')
                {
                    x=i;y=j;
                }
            }
        DFS(x,y);
        if(flag)cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0;
}

2.数芝麻-NC14591

有天糖在宿舍闲着没事, 拿出粉笔在地上画了n*n个方格, 每个方格放上一定数量的芝麻, 并规定如果两个方格相邻且都有芝麻,那这两个方块都属于同一片区域。现在问你的是, 有没有存在这样的区域, 它的芝麻的总数量为m

输入描述:
有多组数据,每组数据第一行为n(n<=100)和m, 接下来有n行n列数据, 每个数字代表每个方格里的芝麻数量,, 当n和m等于0时结束输入。
输出描述:
对于每一组数据,输出一行,如果能找这样的区域, 输出”YES”, 否则输出“NO”。

输入:
3 2
1 0 0
1 0 1
1 0 1
0 0
输出:
YES

#include<bits/stdc++.h>
#include<time.h>
#include<iostream>
#include<ostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<string>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
#define maxn 10000000
typedef long long ll;
const int INF=0x7fffffff;//2147483647
const int SUP=0x80000000;//-2147483648
#define loop(a,b,i) for(int i=a;i<b;i++)
#define loopx(a,b,i) for(int i=a;i<=b;i++)
#define lson (p<<1)
#define rson ((p<<1)|1)
#define mid ((l+r)>>1)
#define MAX 100005
int dfs[505][505];
int D[105][105];
int dir[8][2]={1,0,-1,0,0,-1,0,1};
int n,m,flag,sum;
void DFS(int x,int y)
{
    if(D[x][y]==1||dfs[x][y]==0||x<0||x>=n||y<0||y>=n)return;
    D[x][y]=1;
    sum+=dfs[x][y];
    for(int i=0;i<4;i++)
    {
        int X=x+dir[i][0],Y=y+dir[i][1];
        DFS(X,Y);
    }
}
int main()
{
    while(cin>>n>>m)
    {
        if(n==0&&m==0)break;
        int x,y;
        memset(dfs,0,sizeof(dfs));
        memset(D,0,sizeof(D));
        flag=0;
        for (int i=0;i<n;i++)
            for (int j=0;j<n;j++)
            {
                cin>>dfs[i][j];
            }
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                {
                    sum=0;
                    DFS(i,j);
                    if(sum==m)flag=1;
                }
        if(flag)cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;
}

3.邮票面值设计-NC16813(dp+dfs)

给定一个信封,最多只允许粘贴N张邮票,计算在给定K(N+K≤15)种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大值MAX,使在1至MAX之间的每一个邮资值都能得到。 例如,N=3,K=2,如果面值分别为1分、4分,则在1分~6分之间的每一个邮资值都能得到(当然还有8分、9分和 12分);如果面值分别为1分、3分,则在1分~7分之间的每一个邮资值都能得到。可以验证当N=3,K=2时,7分就是可以得到的连续的邮资最大值,所以 MAX=7,面值分别为1分、3分。

输入描述:
1行。2个整数,代表N,K。
输出描述:
2行。第一行若干个数字,表示选择的面值,从小到大排序。第二行,输出“MAX=S”,S表示最大的面值。

输入:
3 2
输出:
1 3
MAX=7

思路:
1.首先面值里边必有1,故a[1]=1
2.其次直接枚举暴力会超时,故用dp来找到每次搜索的上确界进行搜素剪枝

#include<bits/stdc++.h>
#include<time.h>
#include<iostream>
#include<ostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<string>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
#define maxn 10000000
typedef long long ll;
const int INF=0x7fffffff;//2147483647
const int SUP=0x80000000;//-2147483648
#define loop(a,b,i) for(int i=a;i<b;i++)
#define loopx(a,b,i) for(int i=a;i<=b;i++)
#define lson (p<<1)
#define rson ((p<<1)|1)
#define mid ((l+r)>>1)
#define MAX 100005
char mapp[50][50];
//int dir[8][2]={1,0,-1,0,0,-1,0,1,1,1,1,-1,-1,1,-1,-1};
int n,K;
int a[20];//存储邮票面值
int ans[20];//存储结果
int dp[50000];//储存组成x的邮票最小张数(dp[x])
int maxx=0;//连续邮资最大值
int DP(int k)//找到k种类个数时的连续邮资最大值(搜索剪枝)
{
    memset(dp,63,sizeof(dp));dp[0]=0;
    for(int i=1;i<=k;i++)
        for(int j=a[i];j<=a[k]*n;j++)
            if(dp[j-a[i]]<n)
                dp[j]=min(dp[j],dp[j-a[i]]+1);
    int x=1;
    while(dp[x]<=20)x++;//得到k种时的连续邮资最大值
    return x-1;//返回k种时连续邮资最大值
}
void dfs(int k)//找到满足条件的最大连续邮资数
{
    if(k==K+1)
    {
        int t=DP(K);
        if(t>maxx)
        {
            maxx=t;
            memcpy(ans,a,sizeof(a));
        }
        return ;
    }
    int end=DP(k-1);//前一种类数所能达到的最大连续邮资数
    for(int i=a[k-1]+1;i<=end+1;i++)//枚举并向下搜索
    {
        a[k]=i;dfs(k+1);
    }
}
int main()
{
    cin>>n>>K;
    a[1]=1;
    dfs(2);
    for(int i=1;i<=K;i++)cout<<ans[i]<<" ";
    cout<<endl<<"MAX="<<maxx;
    return 0;
}

4.小H和游戏-NC15158(树+dfs+并查集思想)

小H正在玩一个战略类游戏,她可以操纵己方的飞机对敌国的N座城市(编号为1~N)进行轰炸 敌国的城市形成了一棵树,小H会依次进行Q次轰炸,每次会选择一个城市A进行轰炸,和这座城市距离不超过2的城市都会受损(这里距离的定义是两点最短路径上的边数),轰炸结束后,小H还想知道当前城市A受损的次数 作为游戏的开发者之一,你有义务回答小H的问题

输入描述:
第1行,两个整数N(1≤N≤750000)、Q(1≤Q≤750000)第2到N行,每行两个整数表示树上的一条边第N+1~N+Q行,每行一个整数,表示小H这次轰炸的城市。
输出描述:
输出Q行,每行一个整数表示这一次轰炸的城市在此次轰炸后共计受损几次。

输入:
4 4
1 2
2 3
3 4
1
2
3
4
输出:
1
2
3
3
思路:
定义:直接受损:x直接被轰炸  间接受损:x被相邻节点波及
1.用vector二维数组存储x的所有邻接点vector[x]
2.dfs(x,f)找x的父节点
3.cnt数组含义
cnt[x][0]–自己受损
cnt[x][1]–儿子受损
cnt[x][2]–孙子受损
4.每次询问节点x需要的操作:
①x会波及他的儿子和孙子:cnt[x][1]++;cnt[x][2]++;代表这是x波及的。
②x会波及父亲:cnt[fa[x]][0]++;
③用cnt[fa[x]][1]++;储存x直接受损,用父节点存储原因看5.
④x会波及到爷爷:cnt[fa[fa[x]]][0]++;
5.通过3.可以看出:

①cnt[x][0]代表x的儿子波及给x+x孙子波及给x
②cnt[fa[x]][1]代表x直接受损+x父亲因自身受损波及x受损。
所以用父节点存储儿子的直接受损。
故cnt[fa[x]][1]=x直接受损+x父亲直接受损所波及给x
③cnt[fa[fa[x]]][2]代表x的爷爷波及给x的
综上当前x受损数=x儿子和孙子波及给x的+x直接受损和父亲波及给x的+x爷爷波及给x的=cnt[x][0]+cnt[fa[x]][1]+cnt[fa[fa[x]][2]

#include<bits/stdc++.h>
#include<time.h>
#include<cctype>
#include<iostream>
#include<ostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<string>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
#define maxn 10000000
typedef long long ll;
const int INF=0x7fffffff;//2147483647
const int SUP=0x80000000;//-2147483648
#define loop(a,b,i) for(int i=a;i<b;i++)
#define loopx(a,b,i) for(int i=a;i<=b;i++)
#define lson (p<<1)
#define rson ((p<<1)|1)
#define mid ((l+r)>>1)
#define MAX 100005
//char mapp[1005][1005];
//int dir[8][2]={1,0,-1,0,0,-1,0,1,1,1,1,-1,-1,1,-1,-1};
const int inf = 750010;
int n,q;
int maxx=0;
int cnt[inf][3];//存储该节点破损次数
//(cnt[x][0]--本身 cnt[x][1]--儿子 cnt[x][2]--孙子)
int fa[inf];//x的父亲是谁
vector<int>que[inf];//二维数组,存储树,节点关系
void dfs(int x,int f)//找x的父亲
{
    fa[x]=f;
    for(auto i : que[x])//遍历和x相邻节点
    {
        if(i==f)continue;//排除x的父亲
        else dfs(i,x);
    }
}
void solve()//询问
{
    dfs(1,0);//1的父亲是0
    memset(cnt,0,sizeof(cnt));
    while(q--)
    {
        int x;
        cin >> x;
        //cnt[x][0]--本身 cnt[x][1]--儿子 cnt[x][2]--孙子
        cnt[x][1]++;cnt[x][2]++;//x波及x的儿子和孙子,但不记录x自身
        cnt[fa[x]][0]++; cnt[fa[x]][1]++;//x父亲被波及破损,再将x直接破损记录在父节点上,这样父节点就可以表现x破损情况
        cnt[fa[fa[x]]][0]++;//x爷爷被x波及破损
        int s=cnt[x][0] + cnt[fa[x]][1] + cnt[fa[fa[x]]][2];
        //s=x被儿子或者孙子波及到次数+自身破损次数和受父亲波及+受爷爷波及
        cout<<s<<endl;
    }
}
int main()
{
    cin>>n>>q;
    int x,y;
    for(int i=1;i<=n-1;i++)
        {
            cin>>x>>y;
            que[x].push_back(y);//记录x相邻节点
            que[y].push_back(x);//记录y相邻节点
        }
    solve();
    return 0;
}

5.乌龟跑步-NC15294(四维数组dfs)

有一只乌龟,初始在0的位置向右跑。 这只乌龟会依次接到一串指令,指令T表示向后转,指令F表示向前移动一个单位。乌龟不能忽视任何指令。 现在我们要修改其中正好n个指令(一个指令可以被改多次,一次修改定义为把某一个T变成F或把某一个F变成T)。 求这只乌龟在结束的时候离起点的最远距离。(假设乌龟最后的位置为x,我们想要abs(x)最大,输出最大的abs(x))

输入描述:
第一行一个字符串c表示指令串。c只由F和T构成。第二行一个整数n。1 <= |c| <= 100, 1 <= n <= 50
输出描述:
一个数字表示答案。

输入:
FT
1
FFFTFFF
2
输出:
2
6
思路:
定义四个参数的dfs:①当前字符串位置②剩余操作数③乌龟位置④面朝方向(1右0左)

#include<bits/stdc++.h>
using namespace std;
string h;
int n;
int length;
bool dp[105][55][205][2];
int ans=0;
void dfs(int def,int n,int pos,int way)
{
  if(def==length)//当走到字符串最末尾的时候
  {
      if((n%2)==0) ans=max(ans,abs(pos));
//判断是否剩余步骤数是2的倍数,因为只要是2的倍数,随便选取一个,
//比如F,先改为T,再改为F,每改两次便会回到原来的,
//故只要是2的倍数就能保证和剩余0操作步骤是一样的。
      return;
  }
  if(n<0||dp[def][n][pos][way]) return;//如果操作步数小于0说明上一次改写错误,即没有操作数却改变字符。
//不可以n<=0,因为若n=0,上一步是正确的,可能改了也可能没改,但这一步没有操作数了,只能选择不改。
  dp[def][n][pos][way]=1;//标记目前的状态,下次再到该状态的时候便停止
  if(h[def]=='F')
  {
      if(way==0)
      {
          dfs(def+1,n-1,pos,1);
                    //改变:字符串向前,操作数-1,位置不便(由前进变为改方向),方向变为正向1
          dfs(def+1,n,pos-1,0);
                   //不改变:字符串向前,操作数不变,位置向后退,方向不变保持负方向
      }
      else
      {
          dfs(def+1,n-1,pos,0);
                   //改变:字符串向前,操作数-1,位置不便(由前进变为改方向),方向变为负向0
          dfs(def+1,n,pos+1,1);
                   //不改变:字符串向前,操作数不变,位置向前进,方向不变保持正方向
      }
  }
  else
  {
      if(way==0)
      {
          dfs(def+1,n-1,pos-1,0);
          dfs(def+1,n,pos,1);
      }
      else
      {
          dfs(def+1,n-1,pos+1,1);
          dfs(def+1,n,pos,0);
      }
  }
}
int main()
{
cin>>h>>n;
length=h.size();
memset(dp,0,sizeof(dp));//先初始化为0
dfs(0,n,0,1);//开始字符串位置在0,有n个操作次数,乌龟位置为0,方向为正方向1
cout<<ans<<endl;
}

6.()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值