二维dp

阿里巴巴2023092501

题目描述

在一个 ( n × n ) 的正方形训练场上,每个位置都有一枚硬币。小明从左上角 (0, 0) 出发,跳跃可以按以下方式进行:

  1. 向右走一步,再向上或向下走两步。
  2. 向右走两步,再向上或向下走一步。

小明不能跳出训练场,也不能往回跳。目标是帮助小明获得尽可能多的硬币。

输入描述

  • 第一行输入一个整数 n ( 3 ≤ n ≤ 1000 ),表示训练场的大小。
  • 接下来 n 行,每行输入 n 个整数表示每个位置的硬币数量 ( aij ) ( 1 ≤ ( aij ) ≤ 10⁹ )。

输出描述

  • 输出一个整数表示小明可以获得的最多硬币数量。

样例

输入:

3
1 1 4
5 1 4
1 9 19

输出:

14

限制:
1秒,1024KiB 内存。

思路

二维dp

1、枚举方向数组 :
dx = {1, 1, 2, 2} (向右走一步或两步)
dy = {-2, 2, -1, 1} (向上或向下走两步或一步)

2、状态方程
dp[i][j] : 跳跃到(i,j) 小明可以获得的最多硬币数量为dp[i][j]。
dp[i][j] = max(dp[i+dxi][j+dxi],dp[i][j])
3、初始化
因为dp更新方向始终向右,所以只需要初始化最左边起点,
dp[0][0] = map[0][0]
因为 0 ≤ 1 ≤ ( aij ) , 其余默认初始化为0就可以了

import java.io.*;
import java.util.*;

public class Main {
    static int[][] map;
    static long[][] dp;

    static int[] dy = {-2, 2, -1, 1};
    static int[] dx = {1, 1, 2, 2};

    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(reader.readLine());
        map = new int[n][n];
        for (int i = 0; i < n; i++) {
            String[] split = reader.readLine().split(" ");
            for (int j = 0; j < n; j++) {
                map[i][j] = Integer.parseInt(split[j]);
            }
        }
        dp = new long[n][n];
        for (long[] p : dp) Arrays.fill(p, Long.MIN_VALUE / 2);
        dp[0][0] = map[0][0];
        //外层循环应从 j = 1 开始,这样每次只会考虑向右的移动。
        for (int j = 1; j < n; j++) {
            for (int i = 0; i < n; i++) {
                for (int d = 0; d < 4; d++) {
                    int nx = i - dy[d];
                    int ny = j - dx[d];
                    if (nx < 0 || nx >= n || ny < 0 || ny >= n) {
                        continue;
                    }
                    dp[i][j] = Math.max(dp[i][j], dp[nx][ny] + map[i][j]);
                }
            }
        }
        
        long res = 0L;
        for (int i = 0; i < n; i++) {
            res = Math.max(res, dp[i][n - 1]);
        }
        System.out.println(res);
    }
}

4741魔法百合井

森林里有一口魔法井,井中有 ( L ) 朵百合花。

你可以向井里投入硬币来获取百合花:

  1. 投入 1 个硬币,可以得到 1 朵百合花。
  2. 投入 4 个硬币,记录当前篮子里的百合花数量。
  3. 投入 2 个硬币,得到上次记录数量的百合花。

如果井中百合花数量不足,投入 1 个或 2 个硬币不会有任何效果。

请计算为了获取所有百合花,所需的最少硬币数量。

输入格式

  • 第一行包含整数 ( T ),表示测试数据的组数。
  • 接下来 ( T ) 行,每行包含一个整数 ( L ),表示井中百合花的数量。

输出格式

  • 对于每组数据,输出结果格式为 Case #x: y,其中 ( x ) 为组别编号(从 1 开始),( y ) 为需要的最少硬币数量。

数据范围

  • ( 1 ≤ T ≤ 100 )
  • ( 1 ≤ L ≤ 10^5 )

输入样例

2
5
20

输出样例

Case #1: 5
Case #2: 15

样例解释

  • Case 1: 投入 5 次 1 个硬币,共 5 个硬币。
  • Case 2: 投入 5 次 1 个硬币,再投入 4 个硬币记录,然后 3 次 2 个硬币,共 15 个硬币。

思路

f[i] : 表示获得i朵百合花的最少硬币数量

状态转移方程简化

操作1

选择1可以得到状态转移方程:
f[i] = f[i-1] + 1

操作2

  1. 选择3累加上次记录的百合花。
  2. 要使用选择3,必须先使用选择2。
  3. 使用选择2后可以连续执行选择3。
  4. 设使用选择3的次数为 ( j ),选择3前的百合花数量为 ( i )。i,j∈[1,n]
  5. 使用 ( j ) 次选择3后的百合花总数为 ( (j+1) * i )。
  6. 花费的硬币数量为 ( 4 + 2 * j )。

所以状态转移方程为:

f(j+1) * i = min(f(j+1) * i, fi + 4 + 2 * j)

import java.util.*;
public class Main{
    static int N = (int)1e5 + 10;
    static int[] dp = new int[N];
    static {
        // dp[0] = 0 
        for (int i = 1; i < N; i++) dp[i] = 0x3f3f3f3f;
        for (int i = 1; i < N; i++) {
            dp[i] = Math.min(dp[i], dp[i - 1] + 1);
            for (int j = 1,  i * (j + 1); i * (j + 1) < N; j++) { 
                dp[i * (j + 1)] = Math.min(dp[i * (j + 1)], dp[i] + 4 + 2 * j);
            }
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        for (int i = 1, tol = sc.nextInt(); i <= tol; i++) {
            int n = sc.nextInt();
            System.out.println("Case #" + i +": " + dp[n]);
        }
    }
}

阿里巴巴淘天2023090202

定义一个“特别数组”,满足以下三个条件:

  1. 对于 1 ≤ i ≤ n,有 1 ≤ ai ≤ m
  2. 对于 1 ≤ i ≤ n,ai 是 i 的倍数。
  3. a1 + a2 + ... + an 是 n 的倍数。

给出 n 和 m,求满足条件的“特别数组”数量,对 10^9 + 7 取模。

输入描述:

两个正整数 n 和 m,用空格隔开。
1 ≤ n, m ≤ 1000

输出描述:

特别数组的数量,对 10^9 + 7 取模。

示例:

输入:

3 5

输出:

4

思路:

二维dp+枚举

f[i][j] : 前i个数的总和结果对n取模的结果为j的方案数.
1.对i个数进行枚举,1 ≤ i ≤ n;
2.对前i-1个数可能的总和(j)进行枚举,因为需要对n进行取模(j为n的倍数),所以j∈[0,n)(条件3);
3.对于第i个数ai(1 ≤ ai ≤ m),可能的值为i,2*i,...,n(条件2)
4.dp[i][(j+ai)%n] = (dp[i-1][j] + dp[i][(j+ai)%n])%(1e9+7)

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author 17259
 * @create 2024-06-18 10:32
 */
public class Main {
    static final int mod = (int) 1E9 + 7;

    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String[] split = reader.readLine().split(" ");
        int n = Integer.parseInt(split[0]);
        int m = Integer.parseInt(split[1]);
        System.out.println(solve(n, m));
    }

    private static long solve(int n, int m) {
        long[][] dp = new long[n + 10][m + 10];
        dp[0][0] = 1;
        for (int i = 1; i <= n; i++) {
            // j = dp[i-1][j] : 前i-1个数总和...
            for (int j = 0; j < n; j++) {
                for (int k = i; k <= m; k += i) {
                    dp[i][(j + k) % n] = (dp[i - 1][j] + dp[i][(j + k) % n]) % mod;
                }
            }
        }
        return dp[n][0];
    }
}

携程2023090704

给定一个只包含字符 '0' 和 '1' 的字符串,求该字符串中有多少个“好字串”。“好字串”的定义是其所有前缀中 '0' 的数量严格大于 '1' 的数量。

输入描述

一个只包含 '0' 和 '1' 的字符串,长度不超过 100000。

输出描述

一个整数,表示“好字串”的数量。

示例

输入

10

输出

1

说明

子区间 [2, 2] 组成的子串是一个好串。

输入

11010

输出

2
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String s = scanner.nextLine();
        long res = 0;
        int cnt = 0;
        for(char ch : s.toCharArray()){
            if(ch == '0'){
                cnt++;
            }
            else{
                cnt = Math.max(cnt - 1, 0);
            }
            res += cnt;
        }
        System.out.println(res);
    }
}
posted @ 2024-07-06 13:47  菠萝包与冰美式  阅读(6)  评论(0编辑  收藏  举报