數據結構之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));
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章