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\) 也是唯一确定的。这谁能发现啊

然后你决定不枚举深度了(毕竟状态都没有深度这一维了),改为枚举填了几个数(当然是从大到小排序的)。

转移有以下两种:

  1. 把高的那一层拆成两层,即:\(f_{j,k,p}+s_p\to f_{k+j,j,p}\)(因为往下走了一层,所以代价要加上未被填的数字之和)
  2. 把第 \(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]);
}
posted @ 2024-09-14 16:17  liyixin  阅读(5)  评论(0编辑  收藏  举报