傳送門:hdu 5548 sort
這個題目的標準複雜度是o(n*lgn),據說如果多乘一個lgn優化一個輸入還是可以過得!也就是使用優先隊列的做法!
這個題目還有一個坑點就是不能每次只是貪心,就比如說枚舉到k=4的時候1,2,3,4,5這個數列的最小值是先把前兩個合併變爲數列3,3,4,5然後在進行合併纔是最少的花費,這樣的花費是3+15=18,然而如果按照貪心的思路去做,第一次先枚舉前四個那麼第一次過後數列就變爲10,5這樣的花費就變爲10+10+5=25了,怎麼才能保證結果是花費的最小呢?我們只需要保證最後一次取得是k個就可以了!那麼下一個問題就是怎麼保證最後一次取得是k個呢?
我們來想一下如果按照每次都是取k個來說,每次失去的就是k-1個數,所以我們只需要第一次合併(N-1)%(k-1)+1,{(N-1)%(k-1)這個值爲0的話就不用+1了}就可以了!
那怎麼保證複雜度呢?我們只需要申請兩個隊列就可以,把每次合併完的放到新的隊列裏面,每次取得時候取得是這個兩個隊列的最小值就可以!這樣就nlgn得複雜度了!
AC代碼
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MX = 100000 + 5;
const LL INF = 1e18;
int seq[MX << 2], N;
LL T;
bool check(int n) {
while(!que.empty()) que.pop();
while(!tq.empty()) tq.pop();
int fl = (N - 1) % (n - 1);
int md = fl ? fl + 1 : 0;
int st = N + 1 - (n - md);
if(fl) for(int i=1;i<=n-md;i++) que.push(0);
for(int i = 1; i <= N ; i++) que.push(seq[i]);
LL ans = 0;
while((que.size() + tq.size()) > 1) {
LL tmp = 0;
for(int i = 0; i < n; i++) {
LL A = INF, B = INF;
if(que.empty() && tq.empty()) {
ans += tmp;
goto END;
}
if(!que.empty()) A = que.front();
if(!tq.empty()) B = tq.front();
if(A <= B) {
tmp += A;
que.pop();
} else {
tmp += B;
tq.pop();
}
}
ans += tmp;
tq.push(tmp);
}
END:;
if(ans > T) return false;
return true;
}
void solve() {
int l = 2, r = N, mid,ans=0;
while(l<=r) {
mid = (l + r) >> 1;
if(check(mid)) {
ans = mid;
r = mid-1;
} else l = mid+1;
}
printf("%d\n", ans);
}
int main(void) {
int _;
//freopen("in.txt", "r", stdin);
cin >> _;
while(_--) {
scanf("%d%I64d", &N, &T);
for(int i = 1; i <= N; i++) scanf("%d", &seq[i]);
sort(seq +1, seq +1 + N );
solve();
}
return 0;
}