1.题目链接。题目大意,合并k个数需要付出的代价是这k个数之和,求最小的k使得把n个数合并成一个数的代价最小。
2.看到这个先是模拟了一下,然后就发现了之前似乎做过这种操作。。。。。没错,就是在写压缩的时候写的哈夫曼树。(不懂哈夫曼树的童鞋自己补一下哈)。行了,那这个就是k叉哈夫曼树了。如何构造k叉哈夫曼树呢?我们在二叉哈夫曼树是采用堆来辅助建树,当然是可以的。我们也可以有更加高效的方法,就是采用双队列模拟。这样把数组排个序,剩下的就是O(n)的建树了。然后二分答案即可。还是挺有趣的一个题目的。
#include<bits/stdc++.h>
#include<queue>
#define ll long long
const int N = 1e5 + 5;
int n, m;
int a[N];
using namespace std;
queue<ll>q1, q2;
#pragma warning(disable:4996)
//构建K叉的哈夫曼树
int check(int k)
{
while (!q1.empty())
q1.pop();
while (!q2.empty())
q2.empty();
int num = (n - 1) % (k - 1);
ll t, s = 0;
if (num)
{
for (int i = 0; i < k - 1 - num; i++)
{
q1.push(0);//这个时候,节点是不够的,需要新加节点进入
}
}
for (int i = 1; i <= n; i++)
q1.push(a[i]);
while (true)
{
t = 0;
int x1, x2;
for (int i = 1; i <= k; i++)
{
if (q1.empty() && q2.empty())
break;
if (q1.empty())
{
t += q2.front();
q2.pop();
continue;
}
if (q2.empty())
{
t += q1.front();
q1.pop();
continue;
}
x1 = q1.front();
x2 = q2.front();
if (x1 < x2)
{
t += x1;
q1.pop();
}
else
{
t += x2;
q2.pop();
}
}
s += t;
if (q1.empty() && q2.empty())
break;
q2.push(t);
}
if (s <= m)
return 1;
else
return 0;
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
sort(a + 1, a + n + 1);
int l = 2, r = n;
while (l < r)
{
int mid = (l+r)/ 2;
if (check(mid))
r = mid;
else
l= mid+1;
}
printf("%d\n", r);
}
return 0;
}