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