Openjudge-8782乘积最大(区间dp)

题目:

设有一个长度为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的数字串。输出输出所求得的最大乘积(一个自然数)。(保证最终答案不超过int范围)

样例:

输入:

  4 2

  1231

输出:

  62

这是一道典型的区间dp

代码

#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
/*int pow1(int a)
{
    int res = 1;
    for(int i = 1;i <= a;i++)
    {
        res *= 10;
    }
    return res;
}*/
int main()
{
    char a[47];
    int n, k, sav[45] = {};
    long long dp[45][10] = {}, dl[45][45] = {}, s;
    scanf("%d%d", &n, &k);
    scanf("%lld", &s);
/*    scanf("%s", a);
    int b = strlen(a);
    for(int i = 1;i <= b;i++)
    {
        sav[i] = char(a[i - 1]) - 48;    
    }*/
/*    for(int i = 1;i <= n - 1;i++)
    {
        for(int j = 1;i + j <= n - 1;j++)
        {
            for(int q = 1;q <= j;q++)
            {
                dl[i][j] += sav[i] * pow1(q - 1);    
            }
        }
    }*/
    for(int i = n;i >= 1;i--)
    {
        dl[i][i] = s % 10;
        s /= 10;
    }
    for(int i = 2;i <= n;i++)
    {
        for(int j = i - 1;j >= 1;j--) dl[j][i] = dl[j][i - 1] * 10 + dl[i][i];
    }
    for(int i = 1;i <= n;i++)
    {
        dp[i][0] = dl[1][i];    
    }//初始化 
    for(int k1 = 1;k1 <= k;k1++)
    {
        for(int i = k1 + 1;i <= n;i++)
        {
            for(int j = k1;j < i;j++)
            {
                dp[i][k1] = max(dp[i][k1],dp[j][k1 - 1] * dl[j + 1][i]);    
            }
        }
    }
    printf("%d", dp[n][k]);
    return 0;    
}
//对于每一个区间,都有分段和不分段两种选择
//还要考虑整体分段次数不能超过k
//因此可以猜想需要枚举,分段点的位置,或者段的两端 

ps:注释是自己的错误思路

正确思路是将要讨论的状态设为前i个数中,用了k1个乘号

即计算dp[i][k1]

posted @ 2021-04-15 21:01  Mint-hexagram  阅读(77)  评论(0编辑  收藏  举报