小美的数组合并(美团20240427年暑期实习笔试真题)

题目:小美的数组合并

小美拿到了一个数组,她每次操作可以将两个相邻元素ai合并为一个元素,合并后的元素为原来两个元素之和。小美希望最终数组的最小值不小于k。她想知道有多少种不同的合并结果?

输入描述

第一行输入两个正整数n,k,代表数组大小和数组的最大值。

第二行输入个正整数ai,代表小美拿到的数组。

1<=n,k,ai<=200

输出描述

输出一个整数,代表小美可以得到多少种不同的结果。由于结果可能很大,输出对10^9+7取模的结果。

样例输入

4 4

2 3 4 5

样例输出

4

说明

可能得到的数组有:[5,4,5]、[9,5]、[5,9]、[14]这四种。

题解

记忆化搜索

首先定义:dfs(i,p)表示,从第0到 i-1个数,前面是做完合并操作的。当前从第i个位置的数开始考虑,在这样子的子问题下,有多少种合并方法。并且p表示的是,在前面做完合并的考虑操作之后,第i个数的前一个数是什么(极端情况下第0到i-1个数全合并了,也有可能第i-1个数字没有被合并,保持了原来的样子)。

那么递归的最深处的条件是什么呢?是dfs(n-1,p)吗?其实是dfs(n,p),这个时候表示从第0号元素到第n-1号元素,总共n个数字,都做完合并了,我们要考虑的子问题中的数字个数为0了,那分法就是1,但是这个时候还要看一下我们的p,也就是前面做完合并的考虑操作之后,第n号元素(其实为空因为数组就是从0到n-1)的前一个数是什么,如果这个前一个数字比k小,说明这种分法是不符合要求的,返回0,否则的话是满足要求的,返回1。

那么在dfs的中间层中,对于dfs(i,p),如果p,也就是在第0号元素到第n-1号元素考虑完合并之后,第n号元素(其实为空因为数组就是从0到n-1)的前面的那一个数,比k小,那么说明它如果不和a[i]合并的话是不行的(合并了就一定满足条件吗?交由dfs(i+1,?)去判断吧),那么dfs(i,p)=dfs(i,p+a[i]),而如果p>=k,那么就说明它无论和a[i]合并与否,都是可以的,即dfs(i,p) = dfs(i+1,p+a[i]) + dfs(i+1,p)

最后再在dfs的过程中记忆化一下,把返回值存一下dp数组做一下记忆化搜索。

(代码未经验证,但思路大概是这么个思路)

#include <bits/stdc++.h>
using namespace std;

const int MOD = 1e9 + 7;

vector<vector<int>> dp;
vector<int> arr;
int n, k;

int dfs(int i, int p) {
    if (i == n) {
        return p >= k ? 1 : 0;
    }
    if (dp[i][p] != -1) {
        return dp[i][p];
    }
    int cnt = 0;
    if (p >= k) {
        cnt += dfs(i + 1, arr[i]);
        cnt %= MOD;
        cnt += dfs(i + 1, p + arr[i]);
        cnt %= MOD;
    } else {
    	cnt += dfs(i + 1, p + arr[i]);
        cnt %= MOD;
    }
    dp[i][p] = cnt;
    return cnt;
}