一道区间dp(没有环的合并石子)&一道分段dp(乘积最大)

乘积最大:

今年是国际数学联盟确定的“2000——世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ(爬)也有幸得以参加。活动中,主持人给所有参加活动的选手出了这样一道题目:
设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大。
同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子:
有一个数字串:312, 当N=3,K=1时会有以下两种分法:
1) 3*12=36
2) 31*2=62
这时,符合题目要求的结果是:31*2=62
现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。

输入描述:

第一行共有2个自然数N,K(6 ≤ N ≤ 40,1 ≤ K ≤ 6)
第二行是一个长度为N的数字串。

输出描述:

输出所求得的最大乘积(一个自然数)

 

dp数组 : dp[i][j]表示前i个数用了j个乘号进行计算
状态转移方程:dp[i][j] = max(dp[i][j],dp[w][j-1] * tmp);tmp是 第w+1位 ~ 第i位拼成的数字
#include<bits/stdc++.h>
using namespace std;
# define ll long long
const int maxn = 2e3+100;
ll dp[maxn][maxn];
char str[maxn];
int main()
{
    int n,k;
    scanf("%d %d",&n,&k);
    scanf("%s",str+1);
    int len=strlen(str+1);
    ll tmp=0;
    for(int i=1; i<=len; i++)
    {
        tmp=tmp*10+(str[i]-'0');
        dp[i][0]=tmp;
    }//初始化 
    for(int i=1; i<=len; i++)//前i位 
    {
        for(int j=1; j<= min(k,i-1); j++)//用了j个乘号 
        {
            for(int w=1; w<i; w++)//枚举乘号位置 
            {//这里会出现一种情况:i太大了,因此后面不能加入m个乘号,因此需要判断后半段区间中能放入多少个乘号 
                ll tmp=0;
                for(int jj=w+1; jj<=i; jj++)//因为w是在上一个区间中处理的,因此当前区间中从w+1开始枚举 
                {
                    tmp=tmp*10+(str[jj]-'0');
                }//处理出要乘进去的数 
                dp[i][j]=max(dp[i][j],dp[w][j-1]*tmp);//状态转移(把小区间乘到大区间中) 
            }
        }
    }
    printf("%lld\n",dp[len][k]);//输出 
    return 0;
}

 

合并石子(链状):把洛谷合并石子从环状改为链状(那个蓝题的链状合并石子要用Garsiawachs算法

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int dp[1005][1005], n, sum[4004], a; 
int INF = 0X3f3f3f;
int main()
{
    scanf("%d", &n);
    memset(dp,INF,sizeof(dp));
    for(int i = 1;i <= n;i++)
    {
        scanf("%d", &a);
        sum[i] = sum[i-1] + a;    
        dp[i][i] = 0;
    }
    for(int len = 1;len <= n;len++)//枚举区间长度
    {
        for(int j = 1;j+len <= n+1;j++)//枚举区间起点
        {
            int ends = j + len - 1;//枚举区间结尾
            for(int i = j;i <= n;i++)//枚举区间dp断点
            {
                dp[j][ends] = min(dp[j][ends],dp[j][i] + dp[i+1][ends] + sum[ends] - sum[j-1]);    
            }
        }
    }
    printf("%d", dp[1][n]);
    return 0;    
}
//7 13 7 8 16 21 4 18

 

posted @ 2021-06-10 20:46  Mint-hexagram  阅读(68)  评论(0)    收藏  举报