SS240914A. 神灵庙(desire)
SS240914A. 神灵庙(desire)
问一棵有 \(n\) 个叶子的任意形态的二叉树,左儿子边权是 \(1\),右儿子边权是 \(2\),给叶子任意顺序附上 \(a_i\) 的权值,问 \(\sum dep_i a_i\) 最小。
首先如果树的形态确定了,显然是深度大的叶子选小的 \(a_i\)。(注:这里的深度都是只带权到根的距离)因为是树而且发现 \(n\) 只有 \(750\),所以考虑树形 DP。
\(f_{i,j,k,p}\) 表示考虑到深度为 \(i\),深度为 \(i\) 的有 \(j\) 个叶子,深度为 \(i+1\) 的有 \(k\) 个叶子,已经填了 \(p\) 个数字的最小代价。
然后你发现既然是按深度从小到大枚举的,那么肯定是先填大的数字。而且计算代价可以变成枚举到下一层时加上还没有填写的数字之和。
然后你又发现其实如果你已知了 \(i,j,k\),其实 \(p\) 也是唯一确定的,你只需要枚举三维就好了。
但是由于 \(p\) 不是很好算,你发现其实如果 \(j,k,p\) 确定了,其实 \(i\) 也是唯一确定的。这谁能发现啊
然后你决定不枚举深度了(毕竟状态都没有深度这一维了),改为枚举填了几个数(当然是从大到小排序的)。
转移有以下两种:
- 把高的那一层拆成两层,即:\(f_{j,k,p}+s_p\to f_{k+j,j,p}\)(因为往下走了一层,所以代价要加上未被填的数字之和)
- 把第 \(i\) 个数字填上,即:\(f_{j,k,p}\to f_{j-1,k,p+1}\)
注意这两个转移的顺序问题。
时间复杂度是 \(O(n^3)\),空间复杂度也是。
空间会炸哦。
可以滚动数组,反正我们是枚举填到第几个数,滚掉这一维的空间就好了。
时间不会炸吗??可能常数小不会吧,反正能过。
代码如下:
#include<bits/stdc++.h>
//#define LOCAL
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
const int N=800,inf=0x7f7f7f7f;
int n;
ll a[N];
bool cmp(int a,int b) {return a>b;}
ll f[2][N][N];
int main(){
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("my.out","w",stdout);
#endif
sf("%d",&n);
rep(i,1,n) sf("%lld",&a[i]);
sort(a+1,a+n+1,cmp);
per(i,n-1,1) a[i]+=a[i+1];
memset(f,0x7f,sizeof(f));
f[0][1][0]=0;
int op=1;
rep(i,1,n){
rep(j,0,n-i+1) rep(k,0,n-i+1-j-j) if(f[op^1][j][k]<inf) f[op^1][j+k][j]=min(f[op^1][j+k][j],f[op^1][j][k]+a[i]);
memset(f[op],0x7f,sizeof(f[op]));
rep(j,1,n-i+1) rep(k,0,n-i+1-j) f[op][j-1][k]=min(f[op][j-1][k],f[op^1][j][k]);
op^=1;
}
pf("%lld\n",f[op^1][0][0]);
}
本文来自博客园,作者:liyixin,转载请注明原文链接:https://www.cnblogs.com/liyixin0514/p/18414187