Problem Links: http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1245
题目大意:给出N个节点,每个节点都有一个值f,问如何构造一棵二叉树,使得f[1]*dis[1]+f[2]*dis[2]....*f[n]*dis[n]的值最小。其中dis指的是节点到根的距离。
定义状态dp[i][k]表示从节点i到节点k构造的二叉树的最小值,根据二叉树的性质,只要枚举根节点就可以了。状态转移方程为:
dp[i][k] = min(dp[i][root-1]+weight[i~root-1]+dp[root+1][k]+wegiht[root+1~k]);边界为dp[i][i] = 0,因为根到自己的距离为0。
转移量为O(n),状态为O(n^2),总复杂度为O(n^3)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int inf = 0xfffffff;
const int SIZE = 256;
int n,f[SIZE],sum[SIZE];
int dp[SIZE][SIZE];
int main()
{
while(~scanf("%d",&n))
{
memset(sum,0,sizeof(sum));
for(int i=1; i<=n; i++)
{
scanf("%d",&f[i]);
sum[i] = sum[i-1] + f[i];
}
for(int i=0; i<=n; i++)
for(int k=0; k<=n; k++)
dp[i][k] = inf;
for(int i=1; i<=n; i++)
dp[i][i] = 0;
for(int d=1; d<n; d++)
{
for(int i=1; i+d<=n; i++)
{
int m = i + d;
for(int k=i; k<=m; k++)
{
if(k == i)
dp[i][m] = min(dp[i][m],dp[i+1][m]+sum[m]-sum[i]);
else if(k == m)
dp[i][m] = min(dp[i][m],dp[i][m-1]+sum[m-1]-sum[i-1]);
else
dp[i][m] = min(dp[i][m],dp[i][k-1]+sum[k-1]-sum[i-1]+dp[k+1][m]+sum[m]-sum[k]);
}
}
}
printf("%d\n",dp[1][n]);
}
return 0;
}