题意为给出一组从小到大的数据的频率,要求建一棵二叉查找树,使得总的cost最低。TotalCost= PathLength【i】*fre【i】+...;
题目较简单,一下就看出了状态和转移方程。
开始用记忆化搜索做,最初没有想到将所有任意两个数之间的数的和都求出来保存起来,而是在每一次dp时都去求一次,结果提交后用了6.952s,真是慢死了。然后就想到了在dp之前就应该把和用一个数组存起来,这样应该快一点,不用重复计算。结果还是用了6.144s。(其实这样效果不大,因为用记忆化搜索也没有重复计算,所以效果不佳)这就奇怪了,为什么别人只用了不到1s呢。
然后又想到用递推的思想来优化,后来用了2.276s,虽然快了很多,可还是进不了1s,没法了,能力有限,优化不了了。
记忆化搜索:
#include <iostream>
#include <cstdio>
#include <string.h>
#include <cstring>
using namespace std;
int d[255][255];
int sum[255][255];
int n, fe[255];
int dp( int a, int b)
{
if( d[a][b]!=-1)
return d[a][b];
if( a==b )
return d[a][b] =0;
int i;
d[a][b]= dp( a+1, b) +sum[a][b]-fe[a];
if( dp( a,b-1) +sum[a][b]-fe[b]< d[a][b] )
d[a][b] =dp( a, b-1) +sum[a][b]-fe[b];
for( i=a+1; i<=b-1; i++)
{
int tem =dp( a,i-1) +dp( i+1, b)+ sum[a][b]-fe[i];
if( tem <d[a][b] )
d[a][b]= tem;
}
return d[a][b];
}
int main()
{
while( scanf("%d", &n)!=EOF )
{
int i,j;
for( i=0; i<n; i++)
scanf("%d", &fe[i] );
memset( d, -1, sizeof( d));
memset( sum , 0, sizeof( sum));
for( i=0; i<n ;i++)
{
sum[i][i] =fe[i];
for( j=i+1; j<n; j++)
sum[ i][j] =sum[i][j-1] +fe[j];
}
printf("%d\n", dp(0,n-1) );
}
return 0;
}
递推:
#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
const int maxn =255;
int w[maxn][maxn];
int cost[maxn][maxn];
int f[maxn];
int main()
{
int n;
int i, j,k;
while(scanf("%d", &n)==1)
{
memset(w,0,sizeof(w));
memset(cost,0,sizeof(cost));
for(i=1;i<=n;i++)
scanf("%d", &f[i]);
for(i=1;i<=n;i++)
{
for(j=i;j<=n;j++)
{
w[i][j]=w[i][j-1]+f[j];
}
} //¼ÆËãÀÛ¼ÆÆµÂʺÍ
for(i=2;i<=n;i++)
{
for(j=i-1;j>=0;j--)
{
cost[j][i]=1000000000;
for( k=j;k<=i;k++)
{
if(cost[j][k-1]+cost[k+1][i]+w[j][i]-f[k]<cost[j][i])
{
cost[j][i]=cost[j][k-1]+cost[k+1][i]+w[j][i]-f[k];
}
}
}
}
printf("%d\n", cost[1][n]);
}
return 0;
}