首页 > 试题广场 >

乐团派对

[编程题]乐团派对
  • 热度指数:2796 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

音乐是带给大家快乐的存在,而你的目标就是组建若干支乐队,让世界听到你们的演奏!

你目前有位乐手,每位乐手只能进入一个乐队,但并不是每位乐手都能担大任,因此需要团队合作。第位乐手的能力值为,表示该位乐手所在乐队的人数必须大于等于。在保证每位乐手都被分进一个乐队的情况下,乐队数量最多可以是多少?



输入描述:

第一行一个正整数,表示乐手人数,

第二行个正整数,表示每位乐手的能力值,



输出描述:

输出最多的乐队数量。若无法保证每位乐手都被分进一个乐队,则输出-1。

示例1

输入

4
2 1 2 1

输出

3

组乐队真开心啊!

排序 + 贪心 + DP
首先,最棘手的家伙是那个能力值最大的。如果她没法被满足,那就无解;而如果她能被满足,则一定有解。因为她的乐队可以当做一个「垃圾桶」,不想要的乐手都可以丢进去。

为了满足能力值最大的家伙,我们需要帮她找若干小伙伴。显然这些小伙伴的能力值越大越好。把棘手的家伙们都解决掉,更有利于我们后续的选择。

接下来就可以进入 DP 环节。详见代码:
n = int(input())
arr = list(map(int, input().split()))
arr.sort()
# 无解
if arr[-1] > n:
    print(-1)
# 只能组一个乐队
elif arr[-1] == n:
    print(1)
else:
    # 先贪心地把「垃圾桶」乐队组出来
    arr = arr[:-arr[-1]]
    n = len(arr)
    # dp[i] 表示用前 i + 1 个乐手组乐队的最优结果
    dp = [0] * n 
    dp[0] = 1 if arr[0] == 1 else 0
    for i in range(1, n):
        # 有两种选择:
        # ① 当前乐手丢进垃圾堆,用前面 i - 1 个乐手组乐队
        dp[i] = dp[i - 1]
        # ② 如果能满足当前乐手:贪心地为她组一个乐队,然后用剩下乐手继续组乐队
        if arr[i] <= i + 1:
            dp[i] = max(dp[i], dp[i - arr[i]] + 1)
    print(dp[-1] + 1)


发表于 2026-01-12 07:45:33 回复(2)

咱们有一群小乐手,每个乐手都举着小牌子写着:“我想和至少XX人一起组乐队喵~”

喵喵做的第一件事,就是让大家按牌子的数字从小到大排排坐~ 这样才好安排嘛!

这时候会出现一个根本不可能的情况:

如果那个牌子数字最大的小乐手,他要求的数字比总人数还大……(你这个人满脑子都是自己呢)

“喵喵喵?!所有人陪你都不够,这怎么可能嘛!还不如去打卡拉比丘喵!” 所以直接放弃,输出-1

正常情况下呢,喵喵会用贪心魔法来分组:

先从队伍最前面开始,看第一个小乐手的牌子数字,比如写着3。

那喵喵就想:“好吧,先划出3个人的位置试试~”

但是呀,如果划进去的第3个人,他的牌子写着5——

“喵呀!5个人才够!现在只有3个人,他不开心!”

那喵喵只好把范围扩大,变成5个人的位置……

就这样一直调整,直到这个临时小组能满足小组里所有人的牌子要求~

这样一个小乐队就组成啦!喵喵就记下一笔:“乐队++~”

接下来跳过已经组队的人,继续往后看……

不过喵喵很聪明哦,会提前给要求最高的乐手留位置

所以只在安全范围内组队,不会影响到最后那个最难满足的乐手呢~

最后看看笔记一共组了多少个小乐队,就是答案啦!

喵喵的这个方法,能组出最多的乐队哦~ (≧∇≦)ノ
#include <bits/stdc++.h>
using namespace std;
using ll=long long;

int main() {
    int n;cin >> n;//乐队人数
    vector<ll> man(n);//乐队数组
    for(int i=0;i<n;i++)
    {
        cin >> man[i];
    }
    sort(man.begin(),man.end());//顺序排序(记住这步操作!!!)
    ll ans=1;//一开始就把最大的那一队处理掉

    if(man[n-1]>n)//如果最菜的那个人让所有人和他一组都干不好的话
    {
        cout << -1;//那就别干了!!!
        return 0;
    }

    for(int i=0;i<n-man[n-1];)//i要小于最大的一组的前端
    {
        ll next=i+man[i]-1;//先划到next这个地方
        //next必须比最大一组的前端要小 而且 从i到next人数不够第next号的需求
        //就继续更新next,让next更大
        while(next<n-man[n-1]&&next-i+1<man[next])
        {
            next=i+man[next]-1;
        }
        //next必须比最大一组的前端要小
        if(next>=n-man[n-1]) break;//不然就全并到最大一组去
        ans++;//乐队数增加
        i=next;//更新next
    }
    cout << ans;
}
/*
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣤⡀⣀⣠⣤⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⡀⢀⣴⣾⣷⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣷⣾⣿⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⠿⠛⠛⠉⠉⠉⠉⠉⠉⠛⠻⠿⣿⣿⣿⣿⣿⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⡿⠿⠛⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠿⣿⣿⣿⣷⣄⡀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⣀⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⣰⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⣠⣾⣿⣿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣶⣄⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣻⣿⣿⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢹⣿⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⠁⠈⢢⡀⠀⠀⠀⢸⡇⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣿⡟⠒⢦⡀⠀⠀⠀
⠀⠀⣠⣤⣤⣼⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡇⠀⠀⠀⠉⢢⣄⠀⠀⢿⠊⠳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣷⡄⠀⢷⠀⠀⠀
⠀⢰⠇⠀⣰⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⡌⣹⠗⠦⣬⣇⠀⠉⢢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⡀⢸⡄⠀⠀
⠀⡟⠀⣼⣯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣆⢹⡀⠀⠀⠀⠉⠁⠀⠀⢀⣀⡁⠀⠀⠉⠳⢴⡆⠀⠀⠀⠀⠀⠀⢹⣧⠈⡇⠀⠀
⠀⡇⠀⠀⢻⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣾⠻⠉⠛⠂⠀⠀⠀⠀⠀⠀⠻⠿⣿⣿⣿⣶⣦⡀⠛⣇⠀⠀⠀⠀⠀⣈⣿⠀⡇⠀⠀
⢸⡇⠀⠀⢠⣿⣷⣦⣀⡸⣷⣦⣶⡂⠉⠉⠉⢁⣤⣶⡶⠀⠀⠀⣀⣀⡴⠀⠀⠀⠀⠀⠀⠈⠉⠉⠁⠀⡟⢀⣴⣟⣰⣾⣿⣏⣠⠇⠀⠀
⠈⡇⠀⠀⢸⣿⠁⠉⣿⠛⠛⠃⡇⠀⠀⢠⣶⣿⡿⠛⠁⠀⠀⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠼⢿⠟⠿⢿⡏⠀⠘⣿⡀⠀⠀⠀
⠀⢷⣀⣀⣿⠇⠀⠀⢿⡇⠀⢀⢱⡀⠀⠛⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⠀⠀⢸⠇⠀⠀⢹⣿⣄⠀⠀
⠀⠀⣉⣿⡏⠀⠀⠀⠀⠀⠀⢸⣇⣳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⣿⠃⠀⠀⠀⠀⠀⠀⣿⠈⢧⠀
⠀⠘⣿⣿⠁⠀⠀⠀⠀⠀⠀⠘⣿⡛⣶⠀⠀⣠⠔⠒⠛⠒⠦⡀⠀⠀⠀⠀⣠⡤⠶⠤⢤⣀⠀⠀⠀⢀⣏⡄⠀⠀⠀⠀⠀⡀⣿⡆⠈⣧
⣠⡾⠛⣿⣿⣧⠀⠀⠀⠀⢸⣿⠾⢿⡿⠀⣰⠃⠀⠀⠀⠀⠀⢹⡄⠀⠀⡼⠁⠀⠀⠀⠀⠈⠙⣦⠀⢸⣿⡇⣾⣣⡀⠀⢰⣿⣿⣿⣤⠾
⡟⠀⠀⠻⣿⡟⢷⡄⣤⡀⠈⣿⡀⣸⠇⠀⠏⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⡇⢀⡀⠀⠀⠀⠀⢀⡟⠀⠀⠋⣿⣿⣿⡇⣠⣿⠿⠛⢷⡀⠀
⠀⠀⠀⠀⣿⣇⣨⣿⣿⣿⣦⣽⣷⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠁⠀⠀⠃⠀⠙⠢⠤⠤⠴⢾⠀⠀⠀⠀⢸⣷⣿⣿⠟⠁⠀⠀⠈⣧⠀
⠀⠀⠀⠀⠈⠉⠉⠁⠈⠉⠈⢉⣿⡁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⣿⠀
*/


发表于 2026-01-12 01:27:48 回复(2)

#include<bits/stdc++.h>

using namespace std;

std::mt19937_64 rng(std::chrono::system_clock::now().time_since_epoch().count());
#define int long long 

void solve(){
    int n;cin>>n;
    vector<int> arr(n+1,0);
    for(int i=1;i<=n;i++) cin>>arr[i];
    sort(arr.begin()+1,arr.end());
    if(arr[n]>n){
        cout<<"-1";
        return;
    }
    int ans=1;
    for(int i=1;i<=n-arr[n];i+=arr[i]){
        if(i-1<=n-arr[n]&&i+arr[i]-1<=n-arr[n]) ans++;
    }
    cout<<ans;
}
signed main(){
    std::cin.tie(nullptr)->sync_with_stdio(false);
    int T = 1;
    // std::cin>>T;
    while(T--) solve();
    return 0;
}

发表于 2026-01-12 15:50:31 回复(0)
#include <bits/stdc++.h>
using namespace std;

int main() {   
    int n;
    cin >> n;
    vector<int> a(n+1);
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    sort(a.begin(), a.end());
    if(a.back() > n) {  //保证每个人都要有乐队,如果最后一个人无法分配到乐队中:输出 -1;
        cout << -1 << endl;
        return 0;
    }
    
    int ans = 1;   // 最后一位分一组;然后再从前往后排:保证优先分配能力值低的形成队伍使得数量最多,有保证了每一位成员都能组成队伍(散落的放到最后一组一定符合情况)
    bool failed = false;  // 中间散落的放到最后即可;
    for(int i = 1; i <= n - a.back(); i++) {
        bool failed = true;
        int t = 0;
        for(int j = i; j <= n - a.back(); j++) {  // 从前往后开始加人;
            t++;
            if(t == a[j]) { 
                ans++;
                i = j;
                failed = false;
                break;
            }
        }
        if(failed) break;
    }
    
    cout << ans << "\n";


}

发表于 2026-01-12 14:01:13 回复(0)
想要组建的乐队最多,那么每个乐队的人数要尽可能少。
但乐队人数不能少于队长(能力值最大的乐手)的能力值,那就让他尽可能把能力值大的一波带走。

贪心算法:
将所有乐手按能力值从高到低排队,排在最前的当队长,领走他的队伍。

注意两个特判:
如果当前乐手能力值比剩余人数还高,那他当不了队长,就把他塞进上一个乐队。
如果当前乐手能力值比下一个乐手高2点,那他当队长不划算,也把他塞进上一个乐队。
array<int, 100001> a;
int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];
    sort(&a[0], &a[n] + 1);
    if (a[n] > n) {
        cout << "-1";
        return 0;
    }
    int count = 1;
    n -= a[n];
    while (n > 0) {
        if (a[n] > n || a[n] > a[n - 1] + 1) {
            n--;
        } else {
            count++;
            n -= a[n];
        }
    }
    cout << count;
    return 0;
}
顺便问一下,有些人的答案中写着if(n == 10){ out(6); return 0; }是哪里的“魔法”呀?
发表于 2026-01-12 13:29:21 回复(3)