动态规划之——四边形不等式优化

模型

https://www.luogu.org/problemnew/show/P1880

在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

得出方程:O(n^3)

d p [ i ] [ j ] = m i n ( d p [ i ] [ k ] + d p [ k + 1 ] [ j ] + c o s t [ i ] [ j ] ) dp[i][j]=min(dp[i][k]+dp[k+1][j]+cost[i][j])

四边形优化:O(n^2)

四边形方程: f [ a ] [ c ] + f [ b ] [ d ] < = f [ b ] [ c ] + f [ a ] [ d ] ( a < b < = c < d ) f[a][c]+f[b][d]<=f[b][c]+f[a][d](a < b <= c< d)
在这里插入图片描述

  1. 证明cost满足上式;
  2. cost成立时,dp也会满足上式;
  3. b e s t [ i ] [ j ] = k best[i][j]=k ,表示 d p [ i ] [ j ] dp[i][j] 的最优决策为 [ i , k ] + [ k + 1 , j ] [i,k]+[k+1,j] 。由 1 , 2 1,2 得出 b e s t [ i ] [ j ] best[i][j] 单调:
    1. b e s t [ i ] [ j 1 ] < = b e s t [ i ] [ j ] < = b e s t [ i + 1 ] [ j ] best[i][j-1]<=best[i][j]<=best[i+1][j]
    2. b e s t [ i 1 ] [ j ] < = b e s t [ i ] [ j ] < = b e s t [ i ] [ j + 1 ] best[i-1][j]<=best[i][j]<=best[i][j+1]
  4. 那么做 d p [ i ] [ j ] dp[i][j] 时,只需要从 b e s t [ i ] [ j 1 ] best[i][j-1] 枚举到 b e s t [ i + 1 ] [ j ] best[i+1][j] 即可。

解题

  1. i = a c w [ i ] + i = b d w [ i ] = i = a d w [ i ] + i = b c w [ i ] \sum_{i=a}^cw[i]+\sum_{i=b}^dw[i]=\sum_{i=a}^dw[i]+\sum_{i=b}^cw[i] ,成立;
  2. 对于时间复杂度不够的类似二维dp,推荐 O ( n 3 ) O(n^3) 打表验证,改成四边形优化很方便的;(绝对不是懒得证明 hahaha)
  3. 在更新 d p dp 的时候维护 b e s t best 数组即可。

当然,取最大值显然是一个一个合并最大了。
环形只需要后面再来n个就行

#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL a[209];
LL dp[209][209];
int best[209][209];
LL ma[209][209];

int main(){
    int n;scanf("%d",&n);
    memset(dp,0x3f,sizeof dp);
    for(int i=1;i<=n;i++){
        scanf("%lld",a+i);
        a[i]+=a[i-1];
    }
    for(int i=n+1;i<=2*n;i++){
        a[i]=a[i-1]+a[i-n]-a[i-n-1];
    }
    n*=2;

    for(int i=1;i<n;i++){
        best[i][i+1]=i;
        ma[i][i+1]=dp[i][i+1]=a[i+1]-a[i-1];
    }
    for(int i=1;i<=n;i++){
        dp[i][i]=0;
    }
    LL ans1=1e18,ans2=0;
    for(int len=3;len<=n/2;len++){
        for(int i=1;i+len-1<=n;i++){
            int j=i+len-1;
            ma[i][j]=max(ma[i][j-1],ma[i+1][j])+a[j]-a[i-1];
            for(int k=best[i][j-1];k<=best[i+1][j];k++){
                LL tmp=dp[i][k]+dp[k+1][j]+a[j]-a[i-1];
                if(tmp<dp[i][j]){
                    dp[i][j]=tmp;
                    best[i][j]=k;
                }
            }
            if(len==n/2)ans1=min(ans1,dp[i][j]),ans2=max(ans2,ma[i][j]);
        }
    }
    printf("%lld\n%lld\n",ans1,ans2);
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/90747860
今日推荐