1. 程式人生 > >codevs 1017 乘積最大

codevs 1017 乘積最大

長度 很多 哪裏 set turn 為我 石子歸並 include 鏈接

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 乘積最大