P1388算式

乘号加号
“”
题解:
显然,这道题类似区间dp又不大一样,限制了乘法计算的数量,所以我们采用分段dp,我们用dp[i][j]表示对前i个元素进行j次乘法的最大值并求出元素的前缀和方便计算
代码:

#include<bits/stdc++.h>
using namespace std;
int n,a[105],dp[105][105],sum[105],m;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		sum[i]=sum[i-1]+a[i];
		dp[i][0]=sum[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=min(i-1,m);j++)
			for(int k=1;k<=i;k++) dp[i][j]=max(dp[i][j],dp[k][j-1]*(sum[i]-sum[k]));
	}
	printf("%d",dp[n][m]);
	return 0;	
}

“知识是第一位的”——by New_Traveller
亿点点小问题
当遇到零的时候,这个算法就会变得很难受,因为这个算法是建立在

a+b*c<(a+b)*c

这个数学公式基础上的
但是
这个式子成立的基础是c>=1且a>0
所以当c==0时,便要想个其他方法
那么
之前我们说道这题有一点区间dp的意思,因为加括号并用乘法合并正好符合区间dp的性质,只是有一点限制,那就是区间的个数有限 怎么办?我们观察到这题的数据规模很小,那不妨再个dp的数组再加一个维度,变成这样:fi,j,p表示区间[i,j]中分成p个块时,区间[i,j]在合并后的最大值!
递推部分便会变成这个亚子:

for(p=1;p<=m;p++)                 //枚举区间内的括号块的个数
	for(r=p+1;r<=n;r++)           //枚举区间的宽度
		for(i=1;i+r-1<=n;i++){    //枚举左端点
			j=i+r-1;              //求出右端点
			for(k=i;k<j;k++)      //枚举区间dp中间点
				for(q=0;q<k-i+1&&q<p&&p-q-1<j-k;q++){//这一个循环其实我认为有点问题,下文会说   
				                  //枚举左侧区间中括号块的个数
                                  
					f[i][j][p]=max(f[i][j][p],f[i][k][q]*f[k+1][j][p-q-1]);
					if(p-q<j-k) f[i][j][p]=max(f[i][j][p],f[i][k][q]+f[k+1][j][p-q]);
				}
		}

但是问题又来了:万一右区间长度小于括号块数量怎么办,所以就要再进行一点点改动:

for(p=1;p<=m;p++)
    for(r=p+1;r<=n;r++)
        for(i=1;i+r-1<=n;i++){
            j=i+r-1;
            for(k=i;k<j;k++)
                for(q=max(p-(j-k),0);q<=min(k-i,p);q++){       //就是这一行和之前的程序不一样
                    f[i][j][p]=max(f[i][j][p],f[i][k][q]*f[k+1][j][p-q-1]);
                    if(p-q<j-k) f[i][j][p]=max(f[i][j][p],f[i][k][q]+f[k+1][j][p-q]);
                }
        }

总结一下就是这样:

#include<bits/stdc++.h>
using namespace std;
#define MAXN 51
typedef long long int lli;
lli f[MAXN][MAXN][MAXN];
int nums[MAXN],sum[MAXN];
int i,j,k,m,n,r,t,p,q;
int main(){
#ifndef ONLINE_JUDGE
	freopen("bigexp.in","r",stdin);
	freopen("bigexp.out","w",stdout);
#endif
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++) {
		scanf("%d",nums+i);
	}
	sum[0]=0;
	for(i=1;i<=n;i++) sum[i]=sum[i-1]+nums[i];
	for(i=1;i<=n;i++)
		for(j=i;j<=n;j++) f[i][j][0]=sum[j]-sum[i-1];
	for(p=1;p<=m;p++)
    for(r=p+1;r<=n;r++)
        for(i=1;i+r-1<=n;i++){
            j=i+r-1;
            for(k=i;k<j;k++)
                for(q=max(p-(j-k),0);q<=min(k-i,p);q++){       
                    f[i][j][p]=max(f[i][j][p],f[i][k][q]*f[k+1][j][p-q-1]);
                    if(p-q<j-k) f[i][j][p]=max(f[i][j][p],f[i][k][q]+f[k+1][j][p-q]);
                }
        }
	printf("%lld\n",f[1][n][m]);
	return 0;
}

这样,这个完全不像黄题的题就解决了(完结撒花)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值