小美拿到了一个数组,她每次操作可以将两个相邻元素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;
}