石子合并(经典区间dp)

版权声明:最后一年,加油~ https://blog.csdn.net/zzti_xiaowei/article/details/87114915

[题目]:

n堆石子摆成一条线。现要将石子有次序地合并成一堆,规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将n堆石子合并成一堆的最小代价。

[普通解法]:

状态转移方程: f ( i , j ) = m i n { f ( i , k ) + f ( k + 1 , j ) } + w ( i , j ) f(i,j)=min\{f(i,k)+f(k+1,j)\}+w(i,j)
f ( i , j ) f(i,j) 表示区间 [ i , j ] [i,j] 上的最优值, w ( i , j ) w(i,j) 表示转移时付出的代价。

复杂度:O(n3)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int Max_n=1100;

int n;
int a[Max_n],sum[Max_n];
int dp[Max_n][Max_n];

int main()
{
    scanf("%d",&n);
    sum[0]=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    // 从小区间向大区间进行地推
    for(int d=1;d<n;d++){ //先枚举区间长度
        for(int i=1,j;(j=i+d)<=n;i++){ //再枚举区间起点
            dp[i][j]=inf;
            for(int k=i;k<j;k++)
                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
            dp[i][j]+=sum[j]-sum[i-1];
        }
    }
    printf("%d\n",dp[1][n]);
    return 0;
}

[四边形不等式优化]:

对于一般方程: f ( i , j ) = o p t { f ( i , k ) + f ( k + 1 , j ) } + w ( i , j ) f(i,j)=opt\{f(i,k)+f(k+1,j)\}+w(i,j)
如果 w w 函数满足区间单调性和四边形不等式性质,则 f f 函数也满足四边形不等式性质,具有决策单调性。

  1. 决策单调性:
    定义 s ( i , j ) s(i,j) f ( i , j ) f(i,j) 取得最优值时对应的决策点(即下标),则s(i,j-1)≤s(i,j)<s(i+1,j)
  2. 优化方程:
    f(i,j)=opt{f(i,k)+f(k,j)}+w(i,j) s(i,j-1)≤k≤s(i+1,j),复杂度:O(n2)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int Max_n=1100;

int n;
int a[Max_n],sum[Max_n];
int s[Max_n][Max_n],dp[Max_n][Max_n];

int main()
{
    scanf("%d",&n);
    sum[0]=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];
        s[i][i]=i;
    }
    // 从小区间向大区间进行地推
    for(int d=1;d<n;d++){ //先枚举区间长度
        for(int i=1,j;(j=i+d)<=n;i++){ //再枚举区间起点
            dp[i][j]=inf;
            for(int k=s[i][j-1];k<=s[i+1][j];k++)
                if(dp[i][k]+dp[k+1][j]<dp[i][j]){
                    dp[i][j]=dp[i][k]+dp[k+1][j];
                    s[i][j]=k;
                }
            dp[i][j]+=sum[j]-sum[i-1];
        }
    }
    printf("%d\n",dp[1][n]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zzti_xiaowei/article/details/87114915