2020.07.12日常总结——四边形不等式优化的好题

UVA10304   Optimal   Binary   Search   Tree \color{green}{\texttt{UVA10304 Optimal Binary Search Tree}} UVA10304 Optimal Binary Search Tree

[Problem] \color{blue}{\texttt{[Problem]}} [Problem]

最优排序二叉树问题(OBST,全称如题)的模板题。

给定 n n n 个点的点权 d i ( 1 ≤ i ≤ n ) d_{i}(1 \leq i \leq n) di(1in),你需要给它们建一棵排序二叉树,记第 i i i 个点的深度为 p i ( 1 ≤ i ≤ n ) p_{i}(1 \leq i \leq n) pi(1in),要求这棵排序二叉树:

∑ i = 1 n p i × d i \sum\limits_{i=1}^{n} p_i \times d_i i=1npi×di

的值最小。这个值我们称为最小检索次数。

注意,虽然平衡树的高度最小,但是它不一定是最优的(甚至可能不如一条链优)。

保证 d i ≤ d i + 1 ( 1 ≤ i < n ) d_{i} \leq d_{i+1}(1 \leq i <n) didi+1(1i<n)

[Soluntion] \color{blue}{\texttt{[Soluntion]}} [Soluntion]

我们可以先确定根,再确定左右子树,因为题目保证有序,所以这样做是肯定对的。

f i , j f_{i,j} fi,j 表示为区间 ( i , j ) (i,j) (i,j) 建一棵排序二叉树的最小检索次数。考虑如何计算它。

假设 k ( i ≤ k ≤ j ) k(i \leq k \leq j) k(ikj) 为根,它的深度为 0 0 0,贡献为 0 × d k = 0 0 \times d_k=0 0×dk=0。左子树 ( i , k − 1 ) (i,k-1) (i,k1) 在单独作为一棵树时的最小检索次数为 f i , k − 1 f_{i,k-1} fi,k1,但是因为现在的根是 k k k 了,所以它们各自的深度都增加了 1 1 1,所以总的检索次数要加上:

d i + d i + 1 + d i + 2 + ⋯ + d k − 1 = ∑ t = i k − 1 d t d_{i}+d_{i+1}+d_{i+2}+\cdots +d_{k-1}=\sum\limits_{t=i}^{k-1} d_t di+di+1+di+2++dk1=t=ik1dt

右子树类似,记 c i , j = ∑ t = i j d t c_{i,j}=\sum\limits_{t=i}^{j} d_t ci,j=t=ijdt,则我们有如下转移式:

f i , j = min ⁡ { f i , k − 1 + f k + 1 , j + c i , j − d k } = min ⁡ { f i , k − 1 + f k + 1 , j − d k } + c i , j ( i ≤ k ≤ j ) \begin{aligned} f_{i,j}&=\min\{f_{i,k-1}+f_{k+1,j}+c_{i,j}-d_{k}\} \\ &=\min\{f_{i,k-1}+f_{k+1,j}-d_{k}\}+c_{i,j}\\ (&i \leq k \leq j) \end{aligned} fi,j(=min{fi,k1+fk+1,j+ci,jdk}=min{fi,k1+fk+1,jdk}+ci,jikj)

直接转移是 O ( n 3 ) O(n^3) O(n3) 的,可以用四边形不等式优化到 O ( n 2 ) O(n^2) O(n2),详见参考代码。

[code] \color{blue}{\texttt{[code]}} [code]

int w[260][260],e[260];
int n,f[260][260],p[260];
inline int cost(int a,int b,int c){
	return p[b]-p[a-1]-e[c];
}
int main(){
//	freopen("t1.in","r",stdin);
	while (~scanf("%d",&n)&&n){
		memset(p,0,sizeof(p));
		memset(w,0,sizeof(w));
		memset(f,127,sizeof(f));
		for(int i=1;i<=n;i++){
			scanf("%d",&e[i]);
			p[i]=p[i-1]+e[i];
		}
		for(int i=1;i<=n;i++)
			f[i][i]=0,w[i][i]=i;
		for(int i=1;i<=n;i++)
			f[i+1][i]=f[i][i-1]=0;
		for(int L=2;L<=n;L++)
			for(int j=L;j<=n;j++){
				register int i=j-L+1;
				for(int k=w[i][j-1];k<=w[i+1][j];k++)
					if (f[i][k-1]+f[k+1][j]+cost(i,j,k)<=f[i][j]){
						f[i][j]=f[i][k-1]+f[k+1][j]+cost(i,j,k);
						w[i][j]=k;//四边形不等式中的记录决策点 
					}
			}
		printf("%d\n",f[1][n]);
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值