题目大意:给定一个数字序列,要求在其中插入固定个数的*和加号+,使得算式的结果最大。
题目分析:我们用sum[i]保留前i个数的和,用dp[i][j]保留前i个数中含有j个乘号的最大的结果。那么我们最终要求的是dp[N][K]。显然,dp[i][0]=sum[i],因为没有一个乘号,所以就相当于求前n项和。我们自底向上求解。
以题干中的数据为例子:
N = 5, K=2
五个数分别是:1 2 3 4 5
i|j |
0 |
1 |
2 |
3 |
4 |
1 |
1 |
# |
# |
# |
# |
2 |
3 |
2 |
# |
# |
# |
3 |
6 |
9 |
? |
# |
# |
4 |
10 |
24 |
? |
# |
# |
5 |
15 |
? |
? |
# |
# |
上表表示的是dp[i][j]的值。这张表示如何得到的呢?
首先根据sum[i]可以初始化dp[i][0],然后一列一列求。先求第二列,j=1的情况。
dp[2][1] = dp[1][0] * num[2] = 2
dp[3][1] = max(dp[2][0]*num[3], dp[1][0]*(num[2]+num[3]))
dp[4][1] = max(dp[3][0]*num[4], dp[2][0]*(num[3]+num[4]), dp[1][0]*(num[2]+num[3]+num[4]))
所以,我们得到dp[i][j] = max(dp[i][j], dp[p][j-1]*(sum[i]-sum[p])) (p表示插入的乘号的位置,1<=p<i)
值得注意的是,结果要设置成long long型,因为位数上限是15位,明显超过了int的范围。
代码展示:
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
int main(){
int N,K;
cin>>N>>K;
long long dp[20][20];
int sum[20];
int num[20];
memset(dp,0,sizeof(dp));
memset(sum,0,sizeof(sum));
int temp = 0;
for(int i=1;i<=N;i++){
cin>>num[i];
temp += num[i];
sum[i] = temp;
}
for(int i=1;i<=N;i++)
dp[i][0] = sum[i];
for(int j=1;j<=K;j++){
for(int i=1;i<=N;i++){
if(i>j){ //乘号的数量要小于数字的数量
for(int p=1;p<i;p++){
dp[i][j] = max(dp[i][j],dp[p][j-1]*(sum[i]-sum[p]));
}
}
}
}
cout<<dp[N][K]<<endl;
return 0;
}