动态规划 最大的算式

题目描述

题目很简单,给出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
(1*2+3)*(4+5)=45
……

输入

共有二行,第一行为两个有空格隔开的整数,表示N和K,其中(2<=N<=15, 0<=K<=N-1)。第二行为 N个用空格隔开的数字(每个数字在0到9之间)。

输出

仅一行包含一个整数,表示要求的最大的结果

样例输入

5 2 1 2 3 4 5

样例输出

120

提示

说明

(1+2+3)*4*5=120
分析:典型的动态规划问题嘛。加号就是用来唬人的,因为加号和乘号总个数是N-1,可以保证每两个数中间都一定有一个运算符,所以只考虑乘号就可以了。用f[i][j]表示在前i个数中插入j个乘号所能达到的最大运算和,可以得到状态转移方程f[i][j]=max(f[i][j],f[i-l][j-1]*(sum[i]-sum[l-1]),其中2<=l<=i,sum数组记录的是前i个数的总和。初始值f[i][0]=sum[i],1<=i<=n。


#include<iostream>
using namespace std;
  int n,k;
  long long f[120][120],sum[120];
  int main()
  {
	scanf("%d%d",&n,&k);
	int t;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&t);
		sum[i]=sum[i-1]+t;
	}
	for(int i=1;i<=n;i++)f[i][0]=sum[i];
	for(int i=2;i<=n;i++)
	{
		t=min(i-1,k);
		for(int j=1;j<=t;j++)
		for(int l=2;l<=i;l++)f[i][j]=max(f[i][j],f[l-1][j-1]*(sum[i]-sum[l-1]));
	}
	printf("%lld\n",f[n][k]);
	return 0;
    }

另外一种有点小区别,在关于l取值的时候,上述的范围在2<=l<=i, 下述的范围在2<=l<=n.但是最后运行的结果都是相同的。
#include <iostream>
using namespace std;
#define max(a, b) a > b ? a : b;
long long int dp[16][16];
int sum[16];

int main() {
    int n, k;
    cin >> n >> k;
    for(int i = 1; i <= n; i++) {
        int temp;
        cin >> temp;
        sum[i] =  sum[i-1] + temp;
    }
    for(int i = 1; i <= n; i++) {
        dp[i][0] = sum[i];
    }
    for(int i = 2; i <= n; i++) {
        for(int j = 1; j <= i-1 && j <= k; j++) {
            for(int l = 2; l <= n; l++) {
                dp[i][j] = max(dp[i][j], dp[l-1][j-1] * (sum[i] - sum[l-1]));
            }
        }
    }
    cout << dp[n][k];
    return 0;
}

第三种方法如下,还不太懂。
第三种   提供了另外的一种思路
#include <iostream>
#include <cstring>
#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<k)return f[s][e][k]=m_inf; //如果长度小于乘号个数,返回负无穷 
	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<e;++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<k;++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<<search(1,n,m)<<endl;
  }	
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值