codevs 1017 乘积最大

Posted Herminone

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了codevs 1017 乘积最大相关的知识,希望对你有一定的参考价值。

DP

题目链接:http://codevs.cn/problem/1017/

题目大意:

 

(这次就不派小A小B出场啦2333)

开始我是这样想的(有人跟我思路一样,开心~~23333):

既然是递推的话,我就要从小的区间推到大的区间,

最小的区间当然就是1啦,

然后长度为2的区间也很好推,

直接两个数乘起来(如果乘号个数允许),

那长度为3的呢?

乘号我可以用2个可以用1个,位置也有很多,

可以由长度为2的乘长度为1的,

也可以由长度为1的乘3次(用两个乘号),

现在可以发现,影响结果的是区间内乘号的位置和个数

得出结果的是最后一次运算乘法

那我要想知道一段区间的max,

我只要知道他所有的最后一次运算乘法的结构就可以啦~

那就找出最后一组数乘起来,去递推其他的,

设计状态:dp[i][j][k] 在 i 到 j 区间内,用了k个乘号的max

首先,枚举区间,

然后枚举乘号个数

然后枚举最后一次相乘的位置(乘号的位置),

再枚举左边可能出现的乘号位置和右边可能出现的乘号位置。

(5层for)

Codes:

 

    for(int i = n - 1;i >= 1;-- i)
          for(int j = i + 1;j <= n;++ j)
            for(int k = 1;k <= j - i;++ k) //枚举i --> j区间内的乘号个数 
              for(int l = i;l < j;++ l)//枚举断点 
                for(int m = 0;m <= k - 1;++ m)//枚举左边乘号可能的位置 
                    dp[i][j][k]=max(dp[i][l][m] * dp[l + 1][j][k - m - 1],
                     dp[i][j][k])); cout
<< dp[1][n][k] << \n;

 

 

想想怎么优化?

我还要枚举左边用了几个乘号然后去确定右边用几个显然重复了嘛,

比如:(1) * (2 * 3 * 4) 和(1 * 2) * (3 * 4)答案是一样的

能不能直接规定我右边不用乘号?

(右边的数我们已经处理出来了)

那就不用枚举了左边乘号个数了,

答案是可以!

为什么呢?

因为其实我只要知道最后一次乘在哪里就可以了,

因为我们知道乘法满足交换律啊!!

顺序什么的不重要啊!!(这里和答案与顺序有关的石子归并作对比)

那我为什么要一段一段的去枚举呢!

好的,这样就能去掉一个for啦~

Codes:

  for(int i = n - 1;i >= 1;i --)
      for(int j = i + 1;j <= n;j ++)
        for(int m = 1;m <= k;m ++)// 枚举乘号的个数
          for(int l = i;l <= j;l ++)//枚举断点 
            dp[i][j][m]=max(dp[i][l][m - 1] * dp[l + 1][j][0],dp[i][j][m]); 

这样枚举区间也可以优化掉,

Codes:

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #include<cmath>
 6 
 7 using namespace std;
 8 const int N = 50 + 5;
 9 long long n,k,a,cnt;
10 long long f[N][N];
11 struct zt{
12     long long num[N],len;
13     zt(){
14         memset(num,0,sizeof(num));
15         len = 0;
16     }
17 };
18 long long pow1(long long x,int n){
19     long long ans = 1;
20     while(n){
21         if(n & 1) ans *= x;
22         x *= x,n >>= 1;
23     }
24     return ans;
25 }
26 long long res(int x,int y,long long a){
27     zt b;
28     cnt = 0;
29     while(a){
30         b.num[++ cnt] = a % 10;
31         a /= 10;
32     }
33     b.len = cnt;
34     long long k = (y - x + 1),ans = 0;
35     for(int i = cnt - x + 1;i >= cnt - y + 1;i --){
36         k --;
37         ans += b.num[i] * pow1(10,k);
38     }
39     return ans;
40 }
41 int main(){
42     scanf("%lld%lld",&n,&k);
43     scanf("%lld",&a);
44     for(int i = 1;i <= n;i ++)
45         f[i][0] = res(1,i,a);
46     for(int i = 1;i <= k;++ i){//枚举K个乘号 
47         for(int o = 2;o <= n;++ o)//枚举右端点 
48             for(int j = 1;j < o;++ j){//枚举断点 
49                 f[o][i] = max(f[o][i],f[j][i - 1] * res(j + 1,o,a));
50             }
51     }
52     cout << f[n][k] << \n;
53     return 0;
54 }

MAS:

找到确定一种状态(结果)最少的元素

 

以上是关于codevs 1017 乘积最大的主要内容,如果未能解决你的问题,请参考以下文章

CodeVs 1017 乘积最大(DP)

Codevs_1017_乘积最大_(划分型动态规划/记忆化搜索)

codevs 1017 乘积最大

Codevs P1017 乘积最大

Codevs_1040_[NOIP2001]_统计单词个数_(划分型动态规划)

dp练习——最大乘积