[SCOI2006]zh_tree
一道并不难的树形DP。
由于中序遍历一定,每颗子树内的节点编号一定是连续的。所以考虑枚举根节点,然后递归处理左右两棵子树,求出最小答案后更新即可。数据范围很小,不用做什么优化,把所有状态记录下来记忆化搜索即可。\(O(N^4)\) 可以水过去。
还有对数据与题目描述严重不符的强烈抗议。
#include<bits/stdc++.h>
//#define feyn
const int N=50;
using namespace std;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
int m,pl[N][N][N];
double A,B,a[N],sum,g[N][N][N];//deep,l,r
bool vis[N][N][N];
inline double f(int d,int l,int r){
if(l==r)return (A*d+B)*a[l]/sum;
if(l>r)return 0;
if(vis[d][l][r])return g[d][l][r];
g[d][l][r]=1e9;vis[d][l][r]=true;
for(int i=l;i<=r;i++){
double now=(A*d+B)*a[i]/sum+f(d+1,l,i-1)+f(d+1,i+1,r);
if(now<g[d][l][r])g[d][l][r]=now,pl[d][l][r]=i;
}
return g[d][l][r];
}
void print(int d,int l,int r){
if(l==r){printf("%d ",l);return;}
if(l>r)return;
printf("%d ",pl[d][l][r]);
print(d+1,l,pl[d][l][r]-1);
print(d+1,pl[d][l][r]+1,r);
}
signed main(){
#ifdef feyn
freopen("in.txt","r",stdin);
#endif
read(m);scanf("%lf%lf",&A,&B);
for(int i=1;i<=m;i++){
scanf("%lf",&a[i]);
sum+=a[i];
}
printf("%.3f\n",f(1,1,m));
//print(1,1,m);
return 0;
}
一如既往,万事胜意