您的位置:首页 > 其它

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

2017-03-02 17:04 239 查看
基础版:

有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<c<=d。

(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<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#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<<dp[1]
<<endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: