您的位置:首页 > 其它

我才不是萝莉控呢

2015-09-04 22:26 253 查看

题意

有一个长度为nn 的正整数数组AA,满足Ai≥Ai+1A_i \ge A_{i+1},现在构造一个数组BB,令Bi=∑inAiB_i = \sum\limits_{i}^{n}A_i。

现在,有一个n∗nn * n 的网格图,左下角坐标是(1,1)(1, 1),右上角坐标是(n,n)(n, n)。有一个人正在坐标为(n,1)(n, 1)的位置,每一时刻,如果他现在在(x,y)(x, y),他可以选择走到(x−1,y+1)(x -1,y + 1) 或者(x,⌊y2⌋)(x, \lfloor\frac{y}{2}\rfloor),如果选择后者,他要支付BxB_x的代价。

现在他想走到(1,1)(1, 1),你可以告诉他他支付的代价最少是多少吗?注意在任何时候他都不能离开这个网格图。

分析

引用题解:

这个问题的答案就是数组 A 中所有元素的哈夫曼树的权值。

因为数组是有序的,所以在哈夫曼树中的深度一定是单调不减的。我们考虑每一个位置,把fi,j f_{i,j} 看成现在已经放入了下标比 ii 小的所有节点,剩余的叶子节点有 jj 个。

那么我们每一次有两种选择,

第一种是把所有叶子节点都扩展出两个后继,这时剩下所有节点的深度都增加了11,所以付出的代价是 ∑k=i+1nAk\sum\limits_{k=i+1}^{n}A_k,状态变成了 f2∗i,jf_{2*i,j};

第二种是把第i i 个数填在一个叶子上,这时状态变成了 fi+1,j−1f_{i+1,j−1}。最终的答案就是 min(fn+1,k),(k>0)min( {f_{n+1,k}}),(k > 0)。

我们把这个 DP 过程倒过来,就变成了题目中描述的走路的样子。所以只需要求哈夫曼树就好了,这是经典算法(合并果子)。

代码

用priority_queue略慢…建议用make_heap

#include <cstdio>
#include <queue>
using namespace std;

typedef long long LL;
const int N = 1e5 + 10;
int n,a
;
priority_queue< LL , vector<LL> , greater<LL> > s;

int main() {
int T;
scanf("%d",&T);
while (T --) {
while (!s.empty()) s.pop();
scanf("%d",&n);
for (int i = 1;i <= n;i ++) {
scanf("%d",&a[i]);
s.push(a[i]);
}
LL ans = 0;
for (int i = 1;i < n;i ++) {
LL a = s.top();
s.pop();
LL b = s.top();
s.pop();
s.push(a + b);
ans += a + b;
}
printf("%lld\n",ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: