二维dp
阿里巴巴2023092501
题目描述
在一个 ( n × n ) 的正方形训练场上,每个位置都有一枚硬币。小明从左上角 (0, 0) 出发,跳跃可以按以下方式进行:
- 向右走一步,再向上或向下走两步。
- 向右走两步,再向上或向下走一步。
小明不能跳出训练场,也不能往回跳。目标是帮助小明获得尽可能多的硬币。
输入描述
- 第一行输入一个整数 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 朵百合花。
- 投入 4 个硬币,记录当前篮子里的百合花数量。
- 投入 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
- 选择3累加上次记录的百合花。
- 要使用选择3,必须先使用选择2。
- 使用选择2后可以连续执行选择3。
- 设使用选择3的次数为 ( j ),选择3前的百合花数量为 ( i )。i,j∈[1,n]
- 使用 ( j ) 次选择3后的百合花总数为 ( (j+1) * i )。
- 花费的硬币数量为 ( 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 ≤ i ≤ n,有 1 ≤ ai ≤ m
- 对于 1 ≤ i ≤ n,ai 是 i 的倍数。
- 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);
}
}