题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5884
题目大意:给你n个序列,将其合并为一个,每次合并序列的花费为所合并子序列长度的和,先给你总花费t,问每次最少合并多少个子序列。
思路:排序,每次从最小的开始合并,这样能保证最优。然后二分k即可。有两点需要注意:
1、合并分为很多次,每次合并后变为一个序列,这个序列的长度已经不是最小,需要重新加入到所有剩余序列中去合并(比赛时忽略了这一点,一直把他放在第一位)
2、二分k时,如果(n-1)%(k-1) 大于0,则将其余数+1个数先合并,这样最优。。。。。
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <stack>
#include <queue>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <stdlib.h>
#include <iomanip>
#include <fstream>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define maxn 100005
#define MOD 1000000007
#define mem(a , b) memset(a , b , sizeof(a))
#define LL long long
#define ULL unsigned long long
#define FOR(i , n) for(int i = 1 ; i<= n ; i ++)
typedef pair<int , int> pii;
int t , n , m;
int a[maxn] , sum[maxn];
priority_queue<int , vector<int> , greater<int> >p;
bool solve(int dis)
{
// priority_queue <int> p;
while(!p.empty()) p.pop();
int tmp = dis;
int all = 0;
int mo = 0;
mo = (n - 1) % (dis - 1);
if(mo > 0)
{
mo += 1;
all += sum[mo];
p.push(all);
}
for(int i = mo + 1 ; i <= n ; i ++) p.push(a[i]);
int up = (n - 1) / (dis - 1);
// cout << all << endl;
for(int i = 0 ; i < up ; i ++)
{
int tt = 0;
tmp = dis;
while(tmp --)
{
tt += p.top();
p.pop();
}
all += tt;
p.push(tt);
// cout << all << "ffff" << endl;
}
// cout << all << " " << "YES" << endl;
if(all > m) return false;
return true;
}
int main()
{
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 + 1 + n);
sum[0] = 0;
for(int i = 1 ; i <= n ; i ++)
{
sum[i] = sum[i-1] + a[i];
}
int l = 2 , r = n;
while(l < r)
{
int mid = (l + r) / 2;
if(solve(mid)) r = mid;
else l = mid + 1;
// cout << l << " " << r << endl;
}
if(n <= 1) r = 1;
printf("%d\n" , r);
}
return 0;
}