题目大意:给你n个字符出现的频次,做一棵类似哈夫曼的带权二叉树。但是和哈夫曼树不同的,每个字符的相对位置是不能改变的,在此前提下,使得权和最小。权和 == 每个字符出现的频次*这个字符代表的叶子节点到根的距离。
思路:
用dp[i][j]表示[i,j]这个区间,和成一棵二叉树,最小权和。显然dp[i][j] = min( dp[i][k-1] + dp[k][j] + sum[i][j] ),貌似是n^3的做法,但是很容易发现sum[][]满足四边形不等式,那么用四边形优化则可。关于四边形优化,网上还是很容易搜资料的,这里就不细说了。我感觉四边形优化比斜率优化好写,计算量少一些。
简单说说四边形吧
形如
如果w[][]满足四边形不等式,即:
如果i <= i’ <= j <= j’ ,一定有
这个时候,dp[][]也会满足四边形不等式,即:
dp[i][j] + dp[i’][j’] <= dp[i’][j] + dp[i][j’]
我们定义,dp[i][j]取得最优值的所有k,里面最大的为s[i][j]
由于dp[][]满足四边形不等式,所以有s[i][j-1] <= s[i][j] <= s[i+1][j]
利用s[][]的单调性,我们按dp[][]的长度做dp,可以得到n^2的算法
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef long long LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const LL INF=1LL<<60;
const double INFF=1e100;
const double eps=1e-8;
const int mod=1000000007;
const int NN=2005;
const int MM=1000010;
/* ****************** */
LL dp[NN][NN];
LL sum[NN];
int s[NN][NN];
int tol;
string ans[NN];
char s1[NN];
void dfs(int l,int r,string ss)
{
if(l==r)
{
ans[l]=ss;
return;
}
dfs(l,s[l][r]-1,ss+'0');
dfs(s[l][r],r,ss+'1');
}
int main()
{
int n,i,j,t,l,k;
while(scanf("%d",&n)!=EOF)
{
sum[0]=0;
for(i=1;i<=n;i++)
{
scanf("%d",&t);
sum[i]=sum[i-1]+t;
}
for(l=1;l<=n;l++)
{
for(i=1;i<=n+1-l;i++)
{
j=i+l-1;
if(l==1)dp[i][j]=0;
else if(l==2)
{
s[i][j]=j;
dp[i][j]=sum[j]-sum[i-1];
}
else
{
dp[i][j]=INF;
for(k=s[i][j-1];k<=s[i+1][j];k++)
{
LL temp=dp[i][k-1]+dp[k][j]+sum[j]-sum[i-1];
if(temp<=dp[i][j])
{
dp[i][j]=temp;
s[i][j]=k;
}
}
}
}
}
dfs(1,n,"");
for(i=1;i<=n;i++)
{
for(j=0;j<(int)ans[i].size();j++)
s1[j]=ans[i][j];
s1[j]='\0';
printf("%s\n",s1);
// cout<<ans[i]<<endl;
}
}
return 0;
}