1. 程式人生 > >Ice Cream Tower Gym - 101194D (二分答案、貪心檢驗)

Ice Cream Tower Gym - 101194D (二分答案、貪心檢驗)

題目:傳送門

Solution:

二分答案和貪心檢驗就是這道題思路的核心,我來詳細介紹一下其中的過程,直接算可以製造冰激凌的數量的話很困難,我感覺主要是有一個k的存在使得直接計算不好去想(我是不會的。。),但是檢驗的話就很好操作了,我們可以用二分的方法去一步一步縮小答案區間,最後就可以找到最大值了。

那麼該如何檢驗呢?為了使得數量最大,我們每次肯定挑最小的那個放在頂層,然後找它的下面一層。舉個例子,如果我們要驗證是否能製作x個,我們就先挑出最小的x個來作為頂層,然後從x + 1開始遍歷一遍依次放在這x個的下一層,直到最後。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;
const int maxn = 3 * 1e5 + 100;

ll cake[maxn], a[maxn];
int t, n, k;

bool check(int x)            //貪心檢驗
{
    if(!x)
        return true;
    int j = x + 1;
    for(int i = 1; i <= n; ++i)
         cake[i] = a[i];
    int i;
    for(i = x + 1; i <= n; ++ i)
    {
        while(i <= n && cake[j - x] * 2 > a[i])
            i++;
        if(i > n)
            return false;
        cake[j++] = a[i];
        if((j - 1) / x >= k)    return true;
    }
    if((j - 1) / x >= k)
        return true;
    else
        return false;
}

int Research(int l, int r)       //二分答案
{
    int res = -1;
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(check(mid)) {
            res = mid;
            l = mid + 1;
        }
        else {
            r = mid - 1;
        }
    }
    return res;
}

int main()
{
    //freopen("in.txt", "r", stdin);
    int s = 0;
    cin >> t;
    while(t --) {
        cin >> n >> k;
        for(int i = 1; i <= n; ++ i) {
            scanf("%lld", &a[i]);
        }
        sort(a + 1, a + 1 + n);
        int ans = Research(0, n / k);
        printf("Case #%d: ", ++ s);
        cout << ans << endl;
    }
    return 0;
}