Huffman编码-石子问题+平行四边形优化

基础版:

有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动任意的2堆石子合并,合并花费为将的一堆石子的数量。设计一个算法,将这N堆石子合并成一堆的总花费最小(或最大)。

解法:使用贪心即可


相邻版:

在一条直线上摆着N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为将的一堆石子的数量。设计一个算法,将这N堆石子合并成一堆的总花费最小(或最大)。

题解:设dp[i][j]表示第i到第j堆石子合并的最优值,sum[i][j]表示第i到第j堆石子的总数量。

初始化:

dp[i][i]=0;

dp[i][j]=inf;(i!=j)

(因为是求最小值,初始为inf)

递推方程:dp[i][j]=min(dp[i][k]+dp[k+1][j])+sum[i][j] (i<=k<=j)

该递推公式可以使用平行四边形法则优化:

四边形不等式优化条件

在动态规划中,经常遇到形如下式的转台转移方程:

dp(i,j)=min{dp(i,k-1),dp(k,j)}+sum(i,j)(i≤k≤j)(min也可以改为max)

上述的dp(i,j)表示区间[i,j]上的某个最优值。w(i,j)表示在转移时需要额外付出的代价。该方程的时间复杂度为O(N^3)。


下面我们通过四边形不等式来优化上述方程,首先介绍什么是”区间包含的单调性“和”四边形不等式“

对于下标a<=b

(1)区间包含的单调性如果状态满足满足sum[b][c]<=sum[a][d](可以形象理解为如果小区间包含于大区间中,那么小区间的sum值不超过大区间的sum值)

(2)四边形不等式:sum[a][b]+sum[c][d]<=sum[b][c]+sum[a][d],我们称函数sum满足四边形不等式。(可以形象理解为两个交错区间的w的和不超过小区间与大区间的w的和)

下面给出两个定理

假如sum(i,j)满足四边形条件,那么dp也满足,那么p(i,j)单调,即p(i,j)≤p(i,j+1)≤p(i+1,j+1)。

所以递推公式为:dp[i][j]=min(dp[i][k]+dp[k+1][j])+sum[i][j] (p[i][j-1]<=k<=p[i+1][j])

复杂度为O(n*(n+n))=O(n^2)

#include
#include
#include
#include
#include
#include
#include
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int MAXN=1010;
int dp[MAXN][MAXN];
int p[MAXN][MAXN];
int sum[MAXN];
int a[MAXN];
int main()
{
    int n;
    cin>>n;
    sum[0]=0;
    memset(dp,inf,sizeof(dp));
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        sum[i]=sum[i-1]+a[i];
        dp[i][i]=0;
        p[i][i]=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=p[i][j-1];k<=p[i+1][j];k++)
                if(dp[i][j]>dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
            {
                dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
                p[i][j]=k;
            }
        }
    }
    cout<



你可能感兴趣的:(动态规划)