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:
找到确定一种状态(结果)最少的元素?