zoukankan      html  css  js  c++  java
  • 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
    ", Ans);
        for(int i = 1; i <= n; ++ i)
            if(f[i][i + n - 1] == Ans) printf("%d ", i); 
        return 0;
    }
    
  • 相关阅读:
    linux 常见知识2
    python 数组
    linux 常见知识
    python入门1
    linux 入门的常见命令
    聊天机器人開發好消息!!DIALOGFLOW與微信的天作之合!!
    群发短信软件共冶一爐
    Ultimate Facebook Messenger for Business Guide (Feb 2019)
    Top 5 Business Messaging Announcements at Facebook F8 2019
    Ultimate Guide to WhatsApp for Business 2019
  • 原文地址:https://www.cnblogs.com/whenc/p/13949538.html
Copyright © 2011-2022 走看看