codevs 1017 乘積最大
阿新 • • 發佈:2017-10-25
長度 很多 哪裏 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 乘積最大