问题描述
小C得到了一个数组,他要对这个数组进行一些操作,直到数组为空。每次操作时,他会执行以下步骤:
- 如果数组的第一个元素
a[0]等于0,则直接删除a[0],并将数组中剩余的所有元素向左移动以填补空缺。 - 否则,将
a[0]个a[0]-1添加到数组末尾,并将a[0]减少1。
小C想知道,在进行这些操作直到数组为空时,总共进行了多少次操作。结果需要对 109+7109+7 取模。
测试样例
样例1:
输入:
n = 3 ,a = [2, 3, 4]
输出:257
样例2:
输入:
n = 5 ,a = [1, 1, 1, 1, 1]
输出:15
样例3:
输入:
n = 4 ,a = [10, 5, 3, 7]
输出:68658871
解题思路
这道题目要求我们遵循特定的规则对给定的数组进行操作,直到数组为空。对于题目中的要求可以得知,当一个元素a[i]不为0的时候它会被分割为a[i] 个 a[i]-1,然后对数组继续进行操作。我们可以从中发现一个规律,即每个不为零的元素a[i]的操作次数都为(num + 1) * dp(num - 1) + 1,所以我们可以使用动态规划的方式来解决这个问题。
- 首先定义一个边界条件,当
num为0时,只需要进行一次操作就是将0从数组中删除,所以我们初始化dp(0)=1。 - 如果
num为1,我们需要进行一次操作将1分割为两个0,一个在数组头部,一个在数组末尾,然后这两个0分别需要进行一次删除操作,所以总共有3次操作,即dp(1)=3。 - 如果
num > 1,我们首先要进行一次操作将num分割为num个num-1,然后需要处理num个num-1,所以dp(n) = (n + 1) * dp(n - 1) + 1。代码中可以给1,2,3,4,5的情况给直接列出来,可以节省几次重复计算。 - 最后计算总操作次数,对于给定的数组,对每个元素
a[i]计算dp(a[i]),然后累加,得到总的操作次数,最后将总操作次数对10^9+7取模。
代码实现
function solution(n, a) {
if(n===0) return 0
let MOD = 10 ** 9 + 7
function dp(num) {
if (num === 0) return 1
if (num === 1) return 3
if (num === 2) return 10
if (num === 3) return 41
if (num === 4) return 206
if (num === 5) return 1237
return (num + 1) * dp(num - 1) + 1
}
let totalCount = 0
for (let i = 0; i < n; i++) {
totalCount += dp(a[i])
}
return totalCount % MOD;
}
function main() {
console.log(solution(3, [2, 3, 4]) === 257);
console.log(solution(5, [1, 1, 1, 1, 1]) === 15);
console.log(solution(4, [10, 5, 3, 7]) === 68658871);
}
main();
总结
对于这种数组操作的问题,我们可以先根据规则发现操作的规律,然后根据规律选择合适的方法进行计算。比如这道题是将每个不为0的元素分割为多个比该数小1的数字,那么就可以联想到每个元素的操作都可以由上一个数字的操作次数计算得到,然后就可以总结出规律,使用动态规划的方法实现计算。