Luogu4342 [IOI1998]Polygon(区间DP)题解

题意

合并一堆数,每次合并相加或相乘(题目已给出),设计合并顺序使得最终答案最大

其实就是一个区间DP

\(f_{i, j}\)为合并区间\([i,j]\)的最大价值

加法很好写:\(f_{i, j} = max_{k \geq i}^{k < j}{(f_{i, k} + f_{k + 1, j})}\)

乘法也很好写:\(f_{i, j} = max_{k \geq i}^{k < j}{(f_{i, k} * f_{k + 1, j})}\)

然而

你会发现过不了样例……

??????

仔细想一想,事实上,因为存在负数,所以对于乘法操作,最大值完全可能是由两个极小的负数相乘得到的。因此,我们还需要DP一个最小值。

\(g_{i, j}\)为合并区间\([i,j]\)的最小价值

那么乘法转移应该写成:

f[i][j] = max(f[i][j], f[i][k] * f[k + 1][j]);
f[i][j] = max(f[i][j], f[i][k] * g[k + 1][j]);
f[i][j] = max(f[i][j], g[i][k] * f[k + 1][j]);
f[i][j] = max(f[i][j], g[i][k] * g[k + 1][j]);

\(g\)的转移也是同理的。

完整代码

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int maxn = 110;
int n,f[maxn][maxn],opt[maxn],a[maxn],g[maxn][maxn];
char c[5];

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i){
        scanf("%s%d", c, a + i);
        if(c[0] == 'x') opt[i] = opt[i + n] = 2;
        else opt[i] = opt[i + n] = 1;
    }
    memset(f,-0x3f,sizeof(f));
    memset(g,0x3f,sizeof(g));
    for(int i = 1; i <= n; ++ i) a[i + n] = a[i];
    n *= 2;
    for(int i = 1; i <= n; ++ i) f[i][i] = g[i][i] = a[i];
    for(int l = 1; l < n; ++ l)
        for(int i = 1, j = i + l; i < n && j <= n; ++ i, j = i + l)
            for(int k = i; k < j; ++ k){
                if(opt[k + 1] == 1){
                    f[i][j] = max(f[i][j], f[i][k] + f[k + 1][j]);
                    g[i][j] = min(g[i][j], g[i][k] + g[k + 1][j]);
                } 
                else{
                    f[i][j] = max(f[i][j], f[i][k] * f[k + 1][j]);
                    f[i][j] = max(f[i][j], f[i][k] * g[k + 1][j]);
                    f[i][j] = max(f[i][j], g[i][k] * f[k + 1][j]);
                    f[i][j] = max(f[i][j], g[i][k] * g[k + 1][j]);
                    g[i][j] = min(g[i][j], g[i][k] * g[k + 1][j]);
                    g[i][j] = min(g[i][j], f[i][k] * g[k + 1][j]);
                    g[i][j] = min(g[i][j], g[i][k] * f[k + 1][j]);
                    g[i][j] = min(g[i][j], f[i][k] * f[k + 1][j]);
                } 
            }
    int Ans = -0x3f3f3f3f;
    n /= 2;
    for(int i = 1; i <= n; ++ i) Ans = max(Ans, f[i][i + n - 1]);
    printf("%d\n", Ans);
    for(int i = 1; i <= n; ++ i)
        if(f[i][i + n - 1] == Ans) printf("%d ", i); 
    return 0;
}
posted @ 2020-11-09 17:09  When_C  阅读(111)  评论(0编辑  收藏  举报