题目大意:给定一棵完全性质的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;
}