爱悠闲 > BZOJ 1564 NOI2009 二叉查找树 动态规划

BZOJ 1564 NOI2009 二叉查找树 动态规划

分类: BZOJ  |  标签: BZOJ,BZOJ1564,NOI2009,Treap,动态规划  |  作者: popoqqq 相关  |  发布日期 : 2014-09-24  |  热度 : 73°

题目大意:给定一棵完全性质的treap,定义代价为每个点的访问频率*深度之和 我们可以花K的代价改变一些点的权值 求最小总代价

改变后的权值不能相同 但是由于可以改成任意实数 而且代价与更改的大小无关 所以其实相同与否无所谓了

首先键值是不能更改的 而一棵平衡树的中序遍历保证键值递增 故中序遍历一定 我们先按照键值排序得到中序遍历

w很大 但是保证不重复 所以我们将w离散化

然后就是DP的问题了。。我们令f[i][j][w]表示从i~j的节点构成一棵子树且所有节点权值都大于等于w的最小代价

那么状态转移时枚举新子树的根节点k 有两种更新方式

1.直接将k的权值改为w 则f[i][j][w]=min( f[i][j][w] , f[i][k-1][w] + f[k+1][j][w] + K + i~j的访问频率之和 );

2.若k的权值大于等于w 则f[i][j][w]=min( f[i][j][w] , f[i][k-1][a[k].weight] + f[k+1][j][a[k].weight] + i~j的访问频率之和 );

状态是n^3,枚举k是n,一共O(n^4),n<=70,刚好卡过去

注意DP的顺序以及初值 这题可以写一个记忆化搜索 这样就不用在意顺序和初值的问题了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 100
using namespace std;
int f[M][M][M];
int n,K,ans=0x3f3f3f3f;
struct abcd{
	int value;
	int weight;
	int frequency;
}a[M];
bool operator < (const abcd &x,const abcd &y)
{
	return x.value < y.value;
}
pair<int,int>b[M];
int main()
{
	int i,j,k,w;
	
	cin>>n>>K;
	for(i=1;i<=n;i++)
		scanf("%d",&a[i].value);
	for(i=1;i<=n;i++)
		scanf("%d",&a[i].weight);
	for(i=1;i<=n;i++)
		scanf("%d",&a[i].frequency);
	
	sort(a+1,a+n+1);
	
	for(i=1;i<=n;i++)
		b[i].first=a[i].weight,b[i].second=i;
	sort(b+1,b+n+1);
	for(i=1;i<=n;i++)
		a[b[i].second].weight=i;
	
	for(i=1;i<=n;i++)
		a[i].frequency+=a[i-1].frequency;
	
	memset(f,0x3f,sizeof f);
	for(i=1;i<=n+1;i++)
		for(w=0;w<=n;w++)
			f[i][i-1][w]=0;
	for(w=n;~w;w--)
		for(i=n;i;i--)
			for(j=i;j<=n;j++)
				for(k=i;k<=j;k++)
				{
					//if( &f[i][j][w]==&f[1][4][1] && k==3 )
					//	i++,i--;
					if(a[k].weight>=w)
						f[i][j][w]=min( f[i][j][w] , f[i][k-1][a[k].weight] + f[k+1][j][a[k].weight] + a[j].frequency - a[i-1].frequency );
					f[i][j][w]=min( f[i][j][w] , f[i][k-1][w] + f[k+1][j][w] + K + a[j].frequency - a[i-1].frequency );
				}
	for(w=0;w<=n;w++)
		ans=min(ans,f[1][n][w]);
	cout<<ans<<endl;
}