zoukankan      html  css  js  c++  java
  • codevs 1017 乘积最大

    DP

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

    题目大意:

     给你一个长度为n的数列,加上k个乘号使乘积最大。

    (这次就不派小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] << ' ';

    想想怎么优化?

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

    比如:(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] << '
    ';
    53     return 0;
    54 }

    MAS:

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

  • 相关阅读:
    RSS简易阅读器vb.net源代码
    [建议]有关图片地址的建议
    [音乐欣赏]wind flowers
    有个叫计算机的比我笨
    一个朋友画的建筑设计手绘图与其想到的
    [推荐]华建小翻--QQ里的一个不错的服务商
    爆强的广告
    [学习日记]重构简易RSSREADER的UML类图
    [音乐欣赏]红颜
    [音乐欣赏]花与琴的流星
  • 原文地址:https://www.cnblogs.com/Loizbq/p/7732045.html
Copyright © 2011-2022 走看看