算法题目整合3

133. 夹吃棋

题目描述

在一个 3 * 3 的棋盘上,小红和小紫正在玩“夹吃棋”。 
所谓“夹吃棋”,即如果存在一个白子,它的两侧 (横向或者纵向)相邻都是黑子,则这个棋子将被“夹吃”,对于黑棋亦然。 
如果一个棋盘的局面没有一方被夹吃,或者黑白双方都被对面夹吃,则认为是平局。如果只有一方夹吃了另一方,则认为夹吃方赢,被夹吃方输。 
小红执黑棋,小紫执白棋,现在给定一个局面,请你判断当前棋局是谁获胜。

输入描述

第一行输入一个正整数 t (1 <= t <= 10000),代表询问的次数。 
接下来每组询问输入三行,,每行是一个长度为3的字符串,字符串仅由'o','.','*'组成。
其中 o 代表白棋,* 代表黑棋,. 代表未放置棋子。

输出描述

小红获胜输出“kou”,小紫获胜输出“yukan”,平局输出“draw”。

输入示例

3
...
o*o
...
o**
ooo
..*
o*o
*o*
o*o

输出示例

yukan
kou
draw

思路:模拟

import java.util.*;
class Main{
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        char[][] ch = new char[3][3];
        for(int i = 0; i < n; i++){
            for(int j = 0; j < 3; j++){
                String s = sc.next();
                char[] c = s.toCharArray();
                for(int k = 0; k < 3; k++){
                    ch[j][k] = c[k];
                }
            }
            if(determine(ch) == 0){
                System.out.println("yukan");
            }else if(determine(ch) == 1){
                System.out.println("kou");
            }else{
                System.out.println("draw");
            }
        }
    }
    public static int determine(char[][] ch){
        boolean[] flag = new boolean[3];
        for(int i = 0; i < 3; i++){
            for(int j = 0; j < 3; j++){
                if(ch[i][j] == '.')  continue;
                int i1 = i - 1, i2 = i + 1;
                int j1 = j - 1, j2 = j + 1;
                if(i1 >= 0 && i2 < 3){
                    if(ch[i][j] == '*' && ch[i1][j] == 'o' && ch[i2][j] == 'o')  flag[0] = true;
                    if(ch[i][j] == 'o' && ch[i1][j] == '*' && ch[i2][j] == '*')  flag[1] = true;
                }
                if(j1 >= 0 && j2 < 3){
                    if(ch[i][j] == '*' && ch[i][j1] == 'o' && ch[i][j2] == 'o')  flag[0] = true;
                    if(ch[i][j] == 'o' && ch[i][j1] == '*' && ch[i][j2] == '*')  flag[1] = true;
                }
            }
        }
        if(flag[0] && flag[1])  return 2;
        if(flag[0])  return 0;
        if(flag[1])  return 1;
        return 2;
    }
}

134. 小红买药

题目描述

小红准备买药治病。已知共有 n 种症状和 m 种药,第 i 种药可以治疗一些症状,但可能会导致一些副作用,添加一些新的症状。小红依次服用了一些药,请你告诉小红,当她每次服用一副药时,当前还有多少症状?

输入描述

第一行输入一个正整数 n(1 <= n <= 20),代表症状的数量 
第二行输入一个长应为 n 的 01 串,第 i 位是 "1" 代表小红目前有第 i 个症状,第i位是 "0" 代表没有该症状。 
第三行输入一个正整数 m( 1<= m <= 10^4),代表药的数量。 
接下来的 2 * m 行,每 2 行描述一副药:	
第一行输入一个长度为 n 的 01 串,代表该药能治疗的症状。’1‘代表可以治疗,‘0’代表不能治疗。
第二行输入一个长度为 n 的 01 串,代表该药会产生的副作用。’1‘代表会产生该症状,’0‘代表不会产生。 
接下来的一行,输入一个正整数 q( 1<= q <= 10^4),代表小红服用的药数量。 
接下来的 q 行,每行输入一个正整数 u(1 <= ai, u <= m),代表小红服用了第 u 副药。
保证每副药的副作用产生的症状和该药治疗的症状是不会重复的,即不会存在同一个位置的两个 01 串都是‘1’。

输出描述

输出 q 行,每行输入一个正整数,代表当前小红服用药后,身体有多少症状。

输入示例

4
0101
3
1100
0010
0101
1000
1001
0000
3
2
3
1

输出示例

1
0
1

模拟

import java.util.*;
class Main{
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = Integer.parseInt(sc.nextLine());
        String condition = sc.nextLine();
        char[] cond = condition.toCharArray();
        int numOfDrug = Integer.parseInt(sc.nextLine());
        //ch[i][1][k]表示第i种药能够治疗(1)的症状的第k个字符
        char[][][] ch = new char[numOfDrug][3][n];
        for (int i = 0; i < numOfDrug; i++){
            ch[i][1] = sc.nextLine().toCharArray();
            ch[i][2] = sc.nextLine().toCharArray();
        }
        int q = Integer.parseInt(sc.nextLine());
        for(int i = 0; i < q; i++){
            int u = sc.nextInt();
            for(int j = 0; j < n; j++){
                if(ch[u - 1][1][j] == '1')  cond[j] = '0';
                if(ch[u - 1][2][j] == '1')  cond[j] = '1';
            }
            int cnt = 0;
            for(int j = 0; j < n; j++){
                if(cond[j] == '1')  cnt++;
            }
            System.out.println(cnt);
        }
    }
}

135. 皇后移动的最小步数

题目描述

有一个 n 行 m 列的棋盘,有一些格子是障碍物不能通过。
小红控制一个皇后在从左上角出发,每次移动她可以控制皇后进行以下三种方式中的一种:
1.向右移动若干个格子。
2.向下移动若干个格子。
3.向右下移动若干个格子。
用数学语言描述,当前的坐标在 (x, y) 时,每次移动可以到 (x + k, y) 或 (x, y + k) 或 (x + k, y + k) 其中 k 为任意正整数。
移动的前提是,路径上没有障碍物。 
小红想知道,皇后从左上角移动到右下角,最少要移动多少步?

输入描述

第一行输入两个正整数 n 和 m(1 <= n,m <= 2000),代表行数和列数。 
接下来的 n 行,每行输入一个长度 m 的字符串,用来表示棋盘。
其中 "." 代表可以通过的位置,"*" 代表障碍物。 保证左上角和右下角都不是障碍物。

输出描述

如果无法到达,请输出-1。 
否则输出一个整数,代表最少的移动次数。

输入示例

4 5
...*.
*..**
.....
.....

输出示例

2

很显然的动态规划题, d p [ i ] [ j ] dp[i][j] dp[i][j]表示到第 i i i行第 j j j列需要的最小步数,需要注意表示不可达,所以就需要进行判断三个可能的方向。
rowMin表示从左边到达的最小值,当前位置不是’
'时,每次循环会对其进行更新
colMin表示从上边到达的最小值

import java.util.*;
class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int m = sc.nextInt();
        int n = sc.nextInt();
        char[][] ch = new char[m][n];
        for (int i = 0; i < m; i++) {
            ch[i] = sc.next()
                    .toCharArray();
        }
        System.out.println(solve(ch));
    }

    public static int solve(char[][] ch) {
        int m = ch.length;
        int n = ch[0].length;
        int[][] dp = new int[m][n];
        int[] rowMin = new int[m];
        int[] colMin = new int[n];
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, 0);
        int INF = Integer.MAX_VALUE;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                dp[i][j] = INF;
            }
        }
        for (int i = 1; i < m; i++) {
            rowMin[i] = INF;
        }
        for (int i = 1; i < n; i++) {
            colMin[i] = INF;
        }
        dp[0][0] = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (i == 0 && j == 0) {
                    continue;
                }
                if (ch[i][j] == '*') {
                    dp[i][j] = INF;
                    rowMin[i] = INF;
                    colMin[j] = INF;
                    map.put(j - i, INF);
                    continue;
                }
                if (rowMin[i] != INF) {
                    dp[i][j] = Math.min(dp[i][j], rowMin[i] + 1);
                }
                if (colMin[j] != INF) {
                    dp[i][j] = Math.min(dp[i][j], colMin[j] + 1);
                }
                if (map.getOrDefault(j - i, INF) != INF) {
                    dp[i][j] = Math.min(dp[i][j], map.get(j - i) + 1);
                }
                rowMin[i] = Math.min(rowMin[i], dp[i][j]);
                colMin[j] = Math.min(colMin[j], dp[i][j]);
                map.put(j - i, Math.min(map.getOrDefault(j - i, INF), dp[i][j]));
            }
        }
        return dp[m - 1][n - 1] == INF ? -1 : dp[m - 1][n - 1];
    }
}

144. 字典序最小的 01 字符串

题目描述

小红有一个 01 字符串,她可以进行最多 k 次提作,
每次操作可以交换相邻的两个字符,问可以得到的字典序最小的字符串是什么。

输入描述

第一行包含两个整数,n(1 < n < 10^5)和 k(1 < k < 10^9),
表示字符串的长度和可以进行的操作次数。
接下来一行一个长度为 n 的 01 字符串。

输出描述

输出一个长度为 n 的字符串,表示字典序最小的字符串。

输入示例

5 2
01010

输出示例

00101

思路:模拟

import java.util.*;
class Main{
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        sc.nextLine();
        String s = sc.nextLine();
        char[] ch = s.toCharArray();
        int i = 0;
        for(; i< n;i++){
            while(i != 0 && ch[i] == '0' && ch[i-1] == '1'){
                ch[i] = '1';
                ch[i-1] = '0';
                k--;
                i--;
                if(k == 0){
                    System.out.println(String.valueOf(ch));
                    return;
                }
            }
        }
        System.out.println(String.valueOf(ch));
    }
}

145. 数组子序列的排列

题目描述

讨厌鬼有一个长度为 n (1 < n < 10^5)的数组,他想知道这个数组有多少个子序列是一个排列? 
子序列的定义: 数组删除若干个元素(也可以不删)后得到的新数组。 
排列的定义: 长度为 m 的数组,1 到 m 每个元素都出现过,且恰好出现 1 次。

输入描述

第一行输入一个整数 n。 
第二行输入 n 个正整数,a1, a2, ..., an。(1 <= ai <= 10^9)

输出描述

一行一个整数,表示有多少个子序列是一个排列。由于答案过大,请将答案对 10^ 9 + 7 取模后输出

输入示例

6
1 1 5 2 3 4

输出示例

10

提示信息

符合要求的子序列有:
{1},{1},{1,2},{1,2},{1,2,3},{1,2,3},{1,2,3,4},{1,2,3,4},{1,5,2,3,4},{1,5,2,3,4}
共 10 个

排列的个数等于每个数字出现的个数的乘积,计算每个数个数字出现的乘积,然后再累加即可。

import java.util.*;
class Main{
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr = new int[n];
        for(int i = 0; i < n; i++){
            arr[i] = sc.nextInt();
        }
        long MOD = 1000000007L;
        Map<Integer,Integer> map = new HashMap<>();
        for(int num : arr){
            map.put(num, map.getOrDefault(num, 0)+1);
        }
        long k = 1L;
        long res = 0L;
        for(int i = 1; i <=n; i++){
            if(!map.containsKey(i)){
                break;
            }
            k = (k * map.get(i)) % MOD;
            res = (res + k) % MOD;
        }
        System.out.println(res);
    }
}

146. 传送树

题目描述

小红有一棵传送树,树上有 n (1 <= n <= 10^5) 个节点,编号为 1 到 n,其中 1 号节点为根节点。 
每个节点都有一个传送门,传送门只可以将小红传送到子树中除了自己的其他节点中编号最小的节点。 
小红想知道,经过若干次传送门直到叶子节点为止,可以到达的节点数量是多少。

输入描述

一行一个整数 n,表示树上的节点数量接下来 n - 1 行,每行两个整数 u, v,
表示 u 号节点和 v 号节点之间有一条边(u 指向 v)。

输出描述

输出一行,n 个整数,分别对树上 n 个节点都需要计算答案,
第 i 数字表示小红从第 i 个节点出发,经过若干次传送门到达叶子节点为止,可以到达的节点数量是多少。

输入示例

5
1 4
4 2
4 3
3 5

输出示例

2 1 2 2 1

构造有向图的领接表,然后利用dfs求解,每次选择编号最小的叶子结点+1,返回时也是将最小编号返回(需要和当前start进行比较)

import java.util.*;
public class Main {
    static int[] res;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        res = new int[n + 1];
        List<Integer>[] list = new List[n + 1];
        for(int i = 1; i <= n; i++){
            list[i] = new ArrayList<>();
        }
        for(int i = 1; i < n; i++){
            int u = sc.nextInt();
            int v = sc.nextInt();
            list[u].add(v);
        }
        dfs(list, 1);
        for(int i = 1; i <= n; i++){
            System.out.print(res[i]+" ");
        }
    }
    public static int dfs(List<Integer>[] list, int start){
        if(list[start].size() == 0){
            res[start] = 1;
            return start;
        }
        int min = Integer.MAX_VALUE;
        for(int num : list[start]){
            min = Math.min(min, dfs(list, num));
        }
        res[start] = res[min] + 1;
        return Math.min(start, min);
    }
}

  • 16
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值