数据结构之bfs与dfs(记忆化搜索)

nowcoder F-maze

小明来到一个由n x m个格子组成的迷宫,有些格子是陷阱,用'#'表示,小明进入陷阱就会死亡,'.'表示没有陷阱。小明所在的位置用'S'表示,目的地用'T'表示。

小明只能向上下左右相邻的格子移动,每移动一次花费1秒。

有q个单向传送阵,每个传送阵各有一个入口和一个出口,入口和出口都在迷宫的格子里,当走到或被传送到一个有传送阵入口的格子时,小明可以选择是否开启传送阵。如果开启传送阵,小明就会被传送到出口对应的格子里,这个过程会花费3秒;如果不开启传送阵,将不会发生任何事情,小明可以继续向上下左右四个方向移动。

一个格子可能既有多个入口,又有多个出口,小明可以选择任意一个入口开启传送阵。使用传送阵是非常危险的,因为有的传送阵的出口在陷阱里,如果小明使用这样的传送阵,那他就会死亡。也有一些传送阵的入口在陷阱里,这样的传送阵是没有用的,因为小明不能活着进入。请告诉小明活着到达目的地的最短时间。

输入描述

有多组数据。对于每组数据:
第一行有三个整数n,m,q(2≤ n,m≤300,0≤ q ≤ 1000)。
接下来是一个n行m列的矩阵,表示迷宫。
最后q行,每行四个整数x1,y1,x2,y2(0≤ x1,x2< n,0≤ y1,y2< m),表示一个传送阵的入口在x1行y1列,出口在x2行y2列。

输出描述

如果小明能够活着到达目的地,则输出最短时间,否则输出-1。

示例1

输入

5 5 1
..S..
.....
.###.
.....
..T..
1 2 3 3
5 5 1
..S..
.....
.###.
.....
..T..
3 3 1 2
5 5 1
S.#..
..#..
###..
.....
....T
0 1 0 2
4 4 2
S#.T
.#.#
.#.#
.#.#
0 0 0 3
2 0 2 2

输出

6
8
-1
3

解题思路

比较典型的bfs+优先队列解决迷宫问题

Java版

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main {
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner sc = new Scanner(System.in);
        while(sc.hasNextInt()) {
            int n,m,q;
            n=sc.nextInt();
            m=sc.nextInt();
            q=sc.nextInt();
            char c[][]=new char[n][m];
            int dis[][]=new int[n][m];
            for(int i=0;i<n;i++) {
                c[i]=sc.next().toCharArray();
            }
            Node start=null;
            last:
            for(int i=0;i<c.length;i++) {
                for(int j=0;j<c[i].length;j++) {
                    if(c[i][j]=='S') {
                        start=new Node(i,j,0);
                        dis[i][j]=0;
                        break last;
                    }
                }
            }
            Queue<Node> queue = new LinkedList<Node>();
            queue.add(start);
            Node skip[][]=new Node[n][m];
            for(int i=0;i<q;i++) {
                int t1=sc.nextInt();
                int t2=sc.nextInt();
                int t3=sc.nextInt();
                int t4=sc.nextInt();
                if(c[t1][t2]=='#'||c[t3][t4]=='#') {
                    continue;
                }  
                skip[t1][t2]=new Node(t3,t4);
            }
            Node node;
            int b[][]= {{0,1},{0,-1},{1,0},{-1,0}};
            int result=-1;
            while(!queue.isEmpty()) {
                node=queue.poll();
                int x=node.x;
                int y=node.y;
                int d=node.dis;
                if(c[x][y]=='T') {
                    result=d;
                }
                for(int i=0;i<4;i++) {
                    int t1=x+b[i][0];
                    int t2=y+b[i][1];
                    int t3=d+1;
                    if(t1>=0&&t1<n&&t2>=0&&t2<m&&c[t1][t2]!='#'&&c[t1][t2]!='S'&&(dis[t1][t2]==0||t3<dis[t1][t2])) {
                        queue.add(new Node(t1,t2,t3));
                        dis[t1][t2]=t3;
                    }
                }
                if(skip[x][y]!=null) {
                    int t1=skip[x][y].x;
                    int t2=skip[x][y].y;
                    int t3=d+3;
                    if(dis[t1][t2]==0||t3<dis[t1][t2]) {
                        queue.add(new Node(t1,t2,t3));
                        dis[t1][t2]=t3;
                    }
                }
            }
            System.out.println(result);
        }
    }
}
class Node{
    int x;
    int y;
    int dis;
    public Node(int x, int y) {
        this.x = x;
        this.y = y;
        dis=0;
    }
    public Node(int x, int y, int dis) {
        this.x = x;
        this.y = y;
        this.dis = dis;
    }
     
}

C++版 

#include<bits/stdc++.h>
#define MAXN 1005
using namespace std;
int tx[4]={1,0,-1,0};
int ty[4]={0,1,0,-1};
char mp[MAXN][MAXN];
int dis[MAXN][MAXN];
int n,m;
struct node
{
    int x,y,step;
    friend bool operator <(node a,node b)
    {
        return a.step>b.step;
    }
}cc,dd;
vector<node> vc[MAXN][MAXN];
void bfs(int sx,int sy)
{
    priority_queue<node> que;
    que.push({sx,sy,0});
    dis[sx][sy]=0;
    int flag=0;
    while(!que.empty())
    {
        cc=que.top();
        que.pop();
        if(mp[cc.x][cc.y]=='T')
        {
            printf("%d\n",cc.step);
            flag=1;
            break;
        }
        for(auto c:vc[cc.x][cc.y])
        {
            dd.x=c.x;
            dd.y=c.y;
            if(dd.x>n&&dd.y>m&&dd.x<1&&dd.y<1)
                continue;
            if(cc.step+3<dis[dd.x][dd.y]&&mp[dd.x][dd.y]!='#')
            {
                dis[dd.x][dd.y]=cc.step+3;
                que.push({dd.x,dd.y,cc.step+3});
            }
        }
        for(int i=0;i<4;i++)
        {
            dd.x=cc.x+tx[i];
            dd.y=cc.y+ty[i];
            if(dd.x>n&&dd.y>m&&dd.x<1&&dd.y)
                continue;
            if(cc.step+1<dis[dd.x][dd.y]&&mp[dd.x][dd.y]!='#')
            {
                dis[dd.x][dd.y]=cc.step+1;
                que.push({dd.x,dd.y,cc.step+1});
            }
        }
    }
    if(flag==0)
        printf("-1\n");
}
int main()
{
    int q,sx,sy,a,b,c,d;
    while(scanf("%d%d%d",&n,&m,&q)==3)
    {
        for(int i=1;i<=n;i++)
            scanf("%s",mp[i]+1);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                if(mp[i][j]=='S')
                    sx=i,sy=j;
                dis[i][j]=1e9;
                vc[i][j].clear();
            }
        for(int i=1;i<=q;i++)
        {
            scanf("%d%d%d%d",&a,&b,&c,&d);
            a++,b++,c++,d++;
            vc[a][b].push_back({c,d});
        }
        bfs(sx,sy);
    }
}

LeetCode 329. 矩阵中的最长递增路径

给定一个整数矩阵,找出最长递增路径的长度。

对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。

示例 1:

输入: nums = 
[
  [9,9,4],
  [6,6,8],
  [2,1,1]

输出: 4 
解释: 最长递增路径为 [1, 2, 6, 9]。
示例 2:

输入: nums = 
[
  [3,4,5],
  [3,2,6],
  [2,2,1]

输出: 4 
解释: 最长递增路径是 [3, 4, 5, 6]。注意不允许在对角线方向上移动。

解题思路

比较典型的dfs+记忆化搜索解决搜索问题

Java版

class Solution {
    private int[][] dd={{1,0},{0,1},{-1,0},{0,-1}};
    public int longestIncreasingPath(int[][] matrix) {
        if(matrix.length==0) return 0;
        int row=matrix.length,col=matrix[0].length;
        int[][] vis=new int[row][col];
        for(int i=0;i<row;i++) Arrays.fill(vis[i],-1);
        int ans=0;
        for(int i=0;i<row;i++){
            for(int j=0;j<col;j++){
                ans=Math.max(ans,dfs(matrix,vis,i,j));
            }
        }
        return ans;
    }
    public int dfs(int[][] matrix,int[][] vis,int x,int y){
        if(vis[x][y]!=-1) return vis[x][y];
        int ans=1;
        for(int[] d:dd){
            int xx=x+d[0],yy=y+d[1];
            if(xx>=0&&xx<matrix.length&&yy>=0&&yy<matrix[0].length&&matrix[xx][yy]>matrix[x][y]){
                ans=Math.max(ans,1+dfs(matrix, vis, xx, yy));
            }
        }
        return vis[x][y]=ans;
    }
}

C++版 

class Solution {
public:
     int dfs(vector<vector<int>> &matrix, vector<vector<int>> &memo, int x, int y){
        if(memo[x][y]!=-1) return memo[x][y];
        int dx[] = {1, -1, 0, 0};
        int dy[] = {0, 0, 1, -1};
        int ans = 1;
        for(int i=0; i<4; i++){
            for(int j=0; j<4; j++){
                int tx = x+dx[i], ty = y+dy[i];
                if(tx<memo.size() && tx>=0 && ty>=0 && ty<memo[0].size() && matrix[x][y]>matrix[tx][ty]){
                    ans = max(ans, 1+dfs(matrix, memo, tx, ty));
                }
            }
        }
        return memo[x][y]=ans;
    }
    int longestIncreasingPath(vector<vector<int>>& matrix) {
        int m = matrix.size(); if(m==0) return 0;
        int n = matrix[0].size(); if(n==0) return 0;
        vector<vector<int>> memo(m, vector<int>(n, -1));
        int ans = 1;
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
                ans = max(ans, dfs(matrix, memo, i, j));
            }
        }
        return ans;
    }

};

nowcoder 装箱问题


有一个箱子容量为V(正整数,0 ≤ V ≤ 20000),同时有n个物品(0<n ≤ 30),每个物品有一个体积(正整数)。
要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。

输入描述

1个整数,表示箱子容量
1个整数,表示有n个物品
接下来n行,分别表示这n个物品的各自体积

输出描述

1个整数,表示箱子剩余空间。

示例1

输入

24
6
8
3
12
7
9
7

输出

0

当然这个其实用动态规划解决会更快,这里采用记忆化搜索

#include<bits/stdc++.h>
using namespace std;
int v[35],dp[35][20005];
int dfs(int k,int c)
{
    if(dp[k][c]>=0)
        return dp[k][c];
    if(k==0)
        return 0;
    int ans=dfs(k-1,c);
    if(c>=v[k])
        ans=max(ans,dfs(k-1,c-v[k])+v[k]);
    return dp[k][c]=ans;
}
int main()
{
    int c,n;
    scanf("%d%d",&c,&n);
    for(int i=1; i<=n; i++)
        scanf("%d",&v[i]);
    memset(dp,-1,sizeof(dp));
    printf("%d\n",c-dfs(n,c));
}

dfs-nowcoder Beautiful Numbers

时间限制:C/C++ 8秒,其他语言16秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

NIBGNAUK is an odd boy and his taste is strange as well. It seems to him that a positive integer number is beautiful if and only if it is divisible by the sum of its digits.

We will not argue with this and just count the quantity of beautiful numbers from 1 to N.

输入描述:

The first line of the input is T(1≤ T ≤ 100), which stands for the number of test cases you need to solve.
Each test case contains a line with a positive integer N (1 ≤ N ≤ 1012).

输出描述:

For each test case, print the case number and the quantity of beautiful numbers in [1, N].

示例1

输入

2
10
18

输出

Case 1: 10
Case 2: 12

求1-N中有多少个数能被自身数位之和整除?枚举所有的数位之和,然后记忆化搜索,避免重复dfs

#include<bits/stdc++.h>
typedef long long ll;
#define LL long long
int sum, str[15];
ll dp[13][115][115];
ll dfs(int pos, int now, int re, int flag)
{
    ll ans;
    int u;
    if(pos==0)
        return re==0 && now==sum;
    if(flag==0 && dp[pos][now][re]!=-1)
        return dp[pos][now][re];
    ans = 0;
    u = flag==1?str[pos]:9;
    for(int i=0;i<=u;i++)
        ans += dfs(pos-1, now+i, (re*10+i)%sum, flag&&i==u);
    if(flag==0)
        dp[pos][now][re] = ans;
    return ans;
}
ll calculation(ll n)
{
    ll ans;
    int len;
    len = ans = 0;
    while(n)
    {
        str[++len] = n%10;
        n /= 10;
    }
    for(int i=1;i<=len*9;i++)
    {
        sum = i;
        memset(dp, -1, sizeof(dp));
        ans += dfs(len, 0, 0, 1);
    }
    return ans;
}
int main()
{
    ll n;
    int t, cas = 1;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%lld", &n);
        printf("Case %d: %lld\n", cas++, calculation(n));
    }
}

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值