2023阿里灵犀互娱春招算法题

文章介绍了如何使用动态规划解决一个怪物砍法的编程题目。给定怪物的血量n和可用的k种技能,每种技能可造成1到k点伤害,且可无限次使用,目标是找出恰好杀死怪物的打法数量。文章提供了两种状态转移方程的解法,第一种基于dp[i][j]表示i血量用j次技能打死怪物的方案数,第二种简化后的解法只关注dp[i]表示i血量的方案数。最后给出了两种解法的代码实现和算法复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

原文地址:https://xuedongyun.cn/post/50833/

怪物砍法

题目

题目已经找不到了,只能口述。怪物有n滴血,你有k个技能,每个技能能打1, 2, 3, 4, …, k滴落血。假设每个技能都能用无限次,每个回合用一次技能,不限回合数。问恰好能把怪物打死的打法。

输入描述

输入两个正整数n和k,代表怪物的血量和技能的数量

1 <= n <= 1000

1 <= k <= 100

输出描述

一个整数,代表打法的数量对10^9+7取模的值。

样例输入

3 2

样例输出

3

解法

刚开始做这道题,一直在想打法可能会重复,该如何解决重复的问题。后面想到一种二维dp的方法。假设dp(i, j)表示怪物i滴血,j次打死的打法数量,那么有状态转移方程:

d p [ i ] [ j ] = ∑ k d p [ i − k ] [ j − 1 ] dp[i][j] = \sum_k dp[i-k][j-1] dp[i][j]=kdp[ik][j1]

因为最后一次伤害不同,所以一定不会出现重复的情况。打死一只n滴血的怪物需要的回合数最少为: ⌈ n k ⌉ \lceil \frac n k \rceil kn,最大为: n n n。我们只需要将回合数在这个区间的打法求和即可。

代码实现
long MOD = 1_000_000_007;

k = k > n ? n : k;
int minSkillNum = (int) Math.ceil(n / k);
int maxSkillNum = n;

// i滴血,j刀砍死
long[][] dp = new long[n + 1][maxSkillNum + 1];
// 1刀砍死
for (int i = 1; i <= k; i++) {
    dp[i][1] = 1;
}
// 血只有1滴
dp[1][1] = 1;

for (int i = 2; i <= n; i++) {
    for (int j = Math.max((int) Math.ceil(i / k), 2); j <= i; j++) {
        for (int l = 1; l <= Math.min(k, i-1); l++) {
            dp[i][j] += dp[i - l][j - 1];
            dp[i][j] = dp[i][j] % MOD;
        }
    }
}

long total = 0;
for (int i = minSkillNum; i <= maxSkillNum; i++) {
    total += dp[n][i];
    total = total % MOD;
}

System.out.println(total)

算法复杂度: n × ( k − n k ) × k n \times (k-\frac n k) \times k n×(kkn)×k

更优解法

之前想复杂了,假设dp[i]表示i滴血的打法,那么有状态转移方程:

d p [ i ] = ∑ k d p [ i − k ] dp[i] = \sum_k dp[i-k] dp[i]=kdp[ik]

我们只看最后一击即可,因此绝不会重复

代码实现

一定要考虑怪物0滴血的情况。比如怪物4滴血时,可以用伤害为4的技能一刀打死,此时就会出现加dp[0]的情况

int n = 1000;
int k = 100;

long MOD = 1_000_000_007;

k = k > n ? n : k;

long[] dp = new long[n+1];
// 怪物0滴血,只有一种砍法
dp[0] = 1;
for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= k; j++) {
        if (i - j < 0) {
            break;
        }
        dp[i] += dp[i - j];
        dp[i] = dp[i] % MOD;
    }
}
System.out.println(dp[n]);

算法复杂度: n × k n \times k n×k

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值