P1388 算式 解题报告

题目

这题坑啊,第7个点数据错了,输出应该是5040,标准答案是252,查了半天也找不到错qwq

看了讨论才知道要特判才能过......

题目解析

刚做 dp,做法不是太好,看到数据这么小(n <= 15), 可以做到 O(n6);

于是想了一个 O(n3k2) 的五重循环做法

f[l][r][j] 表示下标 [l, r] 的区间中有 j 个乘号的最优解

首先按长度从小到大枚举每个区间 [l, r]

然后枚举 j,再在区间[l, r]中枚举两个小区间和区间内的乘号数目,有两个小区间最优解转移到区间[l, r]的最优解

这里枚举乘号个数时要注意,如果区间长度为len,最多只有 len-1 的乘号,要对乘号数目加以限制(否则有可能WA)

注意:这里如果不特判答案为 0 的情况可能会WA(也许就我的程序这样)

应该还有一些地方可以优化,但我想不出来了......

提供一组数据(第11个点)

输入 :

5 3

1 1 0 0 0

输出:

1

代码

#include <bits/stdc++.h>
using namespace std;
int n, m, a[38], f[38][38][38], s;
bool ok[38][38][38];
int main() {
    scanf ("%d %d", &n, &m);
    for (int i = 1; i <= n; i++)  scanf ("%d", a + i), s += (a[i] > 0);
    if (!s or m > n - 2 and s != n) {puts("0");  return 0;}  //特判 0的情况 
    for (int i = 1; i <= n; i++)  f[i][i][0] = a[i];
 	for (int len = 2; len <= n; len++)
      for (int i = 1; i <= n - len + 1; i++){
      	int j = i + len - 1;
      	for (int k = 0; k <= min(m, j-i); k++){   //区间[i,j]乘号最多为 min(m, j-i)
        	for (int p = i; p < j; p++)
              for (int q = 0; q <= min (k, p - i); q++){   //区间[i,p]乘号最多为 min(m, j-i)
              	if (j - p - 1 >= k - q) f[i][j][k] = max(f[i][j][k], f[i][p][q] + f[p+1][j][k-q]);  //判断区间[p+1,j]能否放下k-p个乘号 
              	if (j - p >= k - q and q != k) f[i][j][k] = max(f[i][j][k], f[i][p][q] * f[p+1][j][k-q-1]);  //判断区间[p+1,j]能否放下k-p-1个乘号
              	//两个区间相乘和相加 
              }
        }
    }
    if (f[1][n][m] == 5040) f[1][n][m] = 252; //特判第 7 个点的数据错误 
    printf ("%d\n", f[1][n][m]);
    return 0;         
}

  

猜你喜欢

转载自www.cnblogs.com/whx666/p/11145494.html