作者:WINNIE双双围脖_370 | 来源:互联网 | 2023-05-18 16:42
还记得是去年做的DP题目,题目大意如下:给出N个数字,不改变它们的相对位置,在中间加入K个乘号和N-K-1个加号,(括号随便加)使最终结果尽量大。因为乘号和加号一共就是N-1个了,所以恰好每
还记得是去年做的DP题目,题目大意如下:
给出N个数字,不改变它们的相对位置,在中间加入K个乘号和N-K-1个加号,(括号随便加)使最终结果尽量大。因为乘号和加号一共就是N-1个了,所以恰好每两个相邻数字之间都有一个符号。例如:
N=5, K=2,5个数字分别为1、2、3、4、5,可以加成:
1*2*(3+4+5)=24
1*(2+3)*(4+5)=45
。。。
输入
输入文件共有二行,第一行为两个有空格隔开的整数,表示N和K,其中(2<=N<=15, 0<=K<=N-1)。第二行为 N个用空格隔开的数字(每个数字在0到9之间)。
输出
输出文件仅一行包含一个整数,表示要求的最大的结果
样例
BIGEXP.IN
5 2
1 2 3 4 5
BIGEXP.OUT
120 // (1+2+3)*4*5=120
当时记得很清楚,老师给出的转移方程是:
F[i][j]表示前i个数字用j个乘号所有的最大值;
F[i][j]=max{F[i-1][j]+arr[i],[F[i-k][j-1]*sum[i-k+1][i]};
当时就是得了90分(据说在各大oj上能AC)
然后班里的大犇说:方程不对,要三维的区间DP。
去年的我实在太弱了,不提了,这几天突然想起来,没思考多久,代码就想出来了。
题目不能枚举最后一次乘法,这是不对的,遇到多的‘0’就要傻眼了,应该采用分治思想,把一段区间内一分为二,这样能保证得到的答案最大。
#include
#include
#define N 20
#define m_inf -999999
using namespace std;
int f[N][N][N]; //dp三维数组
int sum[N][N]; //求i~j的总和
int arr[N]; //保存数字
int n,m;
void reset() //清空数组
{
memset(f,0,sizeof(f));
memset(arr,0,sizeof(arr));
memset(sum,0,sizeof(sum));
}
int search(int s,int e,int k) //记忆化搜索
{
if(e-s if(f[s][e][k]!=0)return f[s][e][k];
int &maxnum=f[s][e][k];
if(k==0)maxnum=sum[s][e]; //乘号个数为0时即为求和
else
for (int i=s;i {
for(int j=0;j<=k;++j)
maxnum=max(maxnum,search(s,i,j)+search(i+1,e,k-j)); //如果左右相加
for(int j=0;j maxnum=max(maxnum,search(s,i,j)*search(i+1,e,k-j-1));//如果左右相乘
}
return maxnum;
}
int main()
{
while(cin>>n>>m)
{
reset();
for (int i=1;i<=n;++i)
cin>>arr[i];
for (int i=1;i<=n;++i)
for (int j=i;j<=n;++j)
sum[i][j]+=sum[i][j-1]+arr[j];
cout< }
}
给一组有很多0的数据:
15 5
0 1 0 0 1 0 1 0 1 1 1 0 1 1 0
答案是12
如果用错误的枚举最后一次的方法 答案是3