📌  相关文章
📜  可以将数组的最大子集数量划分为最小数量与子集大小的乘积至少为K(1)

📅  最后修改于: 2023-12-03 15:37:08.968000             🧑  作者: Mango

可以将数组的最大子集数量划分为最小数量与子集大小的乘积至少为K

在算法竞赛中,往往需要处理数组的子集问题。而对于某些需要满足一定限制才算合法的子集问题,我们常常可以使用二分答案的思路来解决。本文就将介绍如何对于给定的一个数组,将其最多能划分成多少个和至少为k的子集。

#include<bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;

int n,K,x,ans,mx,a[N],cnt;

inline bool check(int mid){
    cnt = 1; int sum = a[1];
    for(int i = 2;i <= n;++i){
        if(a[i] > mid) return 0;
        if(sum + a[i] <= mid) sum += a[i];
        else sum = a[i],++cnt;
    }
    return cnt <= K;
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin >> n >> K;
    for(int i = 1;i <= n;++i){
        cin >> a[i];
        mx = max(mx,a[i]);
        x += a[i];
    }
    int l = mx,r = x,res =-1;
    while(l <= r){
        int mid = (l + r) >> 1;
        if(check(mid)) res = mid,ans = mid,r = mid - 1;
        else l = mid + 1;
    }
    cout << ans;
    return 0;
}
解释

假设数组为 $a_1,a_2,\ldots,a_n$,我们可以考虑使用二分答案的思路,假设当前二分的答案为 $mid$,我们需要确定是否存在一种划分方案,使得可以将数组分成 $K$ 个子集,每个子集的和都不小于 $mid$。故在 check-mid 中可以按照以下的思路进行划分:

  • 从左往右遍历数组,如果当前元素 $a_i$ 大于 $mid$,则无法将该元素划分到前面的子集中,直接返回 false。
  • 其余情况下,维护一个变量 sum,表示当前子集中元素的和,如果将 $a_i$ 加入当前子集后,和仍不超过 $mid$,则将该元素加入当前子集中。
  • 如果将 $a_i$ 加入当前子集后,和已经超过了 $mid$,则新建一个子集,并将 $a_i$ 加入其中。

最终,如果我们能够划分出不多于 $K$ 个大小至少为 $mid$ 的子集,则说明答案可以更小一些;否则,答案必须更大一些。

具体的详细解释可以见代码注释。