【牛客小白月赛27:贪心 | DP |(详解)】B:乐团的派对

乐团派对

https://ac.nowcoder.com/acm/contest/6874/B

牛客小白月赛27:B题 乐团的派对

【难度】


鄙人不才,WA了8发。。

【题意】

你有 个人,**每个人有能力值 ,表示该人所在的队伍人数必须大于等于 **

保证每个人都被分进一个队的情况下,队伍数量最多是多少?无解输出

【数据范围】


【样例输入】


4
2 1 2 1

【样例输出】

3

【解释】

队伍1:第一个人、第三个人
队伍2:第二个人
队伍3:第三个人

【思路】

(一)首先考虑贪心

首先判断可行性。易得,若 则无解,输出
接下来,人怎么分组?
既然是贪心,易想到我们需要对能力值进行排序。

(1)逆序贪心?

【做法】
我们把 进行循环遍历。
若对于第 个人能力值为 是接下里还没分好组的人。
接下来,我们选择 ,把这些人组成一支队伍。
,则可以划分队伍,因为易得
,则无法划分队伍,就把他们合并到人数最多的队伍。

【结果】
可以AC


有反例可以
比如
按照该贪心算法,分组为,为两组。
但是正确的分组应该为 ,为六组。
牛客数据水了

【结论】

(2)顺序贪心?

【做法】
我们把 进行循环遍历。
若对于第 个人能力值为 是接下里还没分好组的人。
接下来,我们选择 ,把这些人组成一支队伍。
,则无法划分队伍,就把他们合并到人数最多的队伍。

【结果】


该算法 当然不一定成立。
比如分组
按该算法的分组为 ,为两组。但是分组不满足题目要求呀!
但是正确的分组应该为 ,为一组。

【结论】

(3)改良顺序贪心?

【做法】
我们把 进行循环遍历。
若对于第 个人能力值为 是接下里还没分好组的人。
接下来,我们选择 ,把这些人组成一支队伍。
,我们继续选择
直到第一个合法的 满足
若 找不到合法的 ,则这些人与最大人数的队伍合并。


该算法
比如分组
按该算法, 枚举到时,目前分组为
但是遇到 ,我无法选出5个人来,也无法在某个已有团队中直接把这个人给塞进去。
我们还要再计算最少数目的已有团队,使得团队总人数
怎么变成背包问题了??这可是贪心做法!

【结论】

(4)正解贪心

【做法】

升序排序后,**我们先把 安排掉**,易得至少要 个人。
我们安排 这些下标的人: 成立,所以该选择一定是合法的、最贪心的。

接下来,我们把 进行循环遍历。与做法3的贪心选择相同:

若对于第 个人能力值为 是接下里还没分好组的人。
接下来,我们选择 ,把这些人组成一支队伍。
,我们继续选择
直到第一个合法的 满足
若 找不到合法的 ,则这些人与最大人数的队伍合并。

【核心代码】

时间复杂度:
就是排序+for循环的时间复杂度。

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 1e5+50;
int aa[MAX];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&aa[i]);
    sort(aa+1,aa+1+n);
    int ren = 1;            /// ren表示目前该队伍中有多少人,默认值为 1
    int ans = 1;
    if(aa[n] > n)printf("-1");
    else{
        for(int i=1;i<=n - aa[n];++i){
            while(i<=n - aa[n] && ren < aa[i]){
                int cha = aa[i] - ren;
                ren += cha;
                i += cha;
            }
            if(i > n - aa[n])break;    /// 找不到合法的‘k’,与最大队伍合并,答案不变
            ans++;            /// 找到了合法的‘k’,新的队伍产生
            ren = 1;
        }
        printf("%d",ans);
    }
    return 0;
}

(二)其次考虑DP做法

首先,我们一样升序排序。
对于第 个人我们可以把它合并在第 个人的团队里面(如果存在的话)。
或者,我们**选择 下标的人拉成新的一个队伍**。
当然这时我们选择的是 ,稍微想一想就能得到了。

那么状态转移方程就得到了:


【感谢热心网友的Hack!】

【核心代码】

时间复杂度:
就是排序+for循环的时间复杂度。
人的本质是复读机

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 2e5+50;
int aa[MAX];
int dp[MAX];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&aa[i]);
    sort(aa+1,aa+1+n);
    int t;
    for(int i=1;i<=n;++i){
        int di = (i - aa[i]);
        t = 0;                        /// 选 [di+1,i]的人的时候的dp[i]的值
        if(di >= 0)t = dp[di] + 1;
        dp[i] = max(t,dp[i-1]);       /// 向左合并
    }
    printf("%d",t==0 ? -1 : t);       /// 为了保证合法,最后一支队伍要选择区间[n-a[n]+1,n]的范围
    return 0;
}
全部评论
大佬分析的很到位,但是dp做法好像有瑕疵,可以试试 3 1 1 3 这组数据(菜鸡一枚,有错轻喷)
1 回复 分享
发布于 2020-08-24 21:58
12 6 6 6 6 6 6 6 1 1 1 1 1 这组样例我可以hack多少人 _(;3亅<)_
点赞 回复 分享
发布于 2020-08-23 17:39

相关推荐

昨天 11:19
已编辑
门头沟学院 Java
已经一年没发牛客了,为什么呢,因为没脸发...&nbsp;一年前的我自认为在25届中技术一流,八股无敌,项目出色,但是一年校招的蹉跎让我差点转行。24年春招收割了十几个实习&nbsp;offer&nbsp;之后我去了某家大厂实习到9月份转正失败,那时候的我还没有意识到噩梦将来,7月因为投秋招提前批没反馈,于是开始投了几个实习转正岗位练手又拿了3个中大厂&nbsp;offer,这时的我沉浸在我自以为是的骄傲里。9月秋招正式批开始后我几乎把我能找到的所有的岗位都投了一遍,只收获了大厂海笔,0面试。10月份第一家给我面试的公司是数字马力(蚂蚁的内包),诚恳的说,当时收到这家面试是嚣张的,觉得我拿这个&nbsp;offer&nbsp;如探囊取物,就当个保底吧。...
中街牛奶提子:是啊,不应该在秋招的时候继续投实习岗。也劝26届的,八月末后,实习岗就不应该投,给人错误的行情认知。佬是学院本,觉得约面难,双非何尝不是一样呢,秋招战场的激烈和实习完全不同。当时我秋招的时候也是边面实习,当时面实习面一个过一个觉得自己很优越,觉得能收获一堆实习offer那秋招肯定也行。为什么要在秋招拿一堆实习offer增强自己所谓的虚荣心,当时就是贱,为了所谓的攀比虚荣心
点赞 评论 收藏
分享
01-17 08:34
门头沟学院 Java
点赞 评论 收藏
分享
评论
12
1
分享

创作者周榜

更多
牛客网
牛客企业服务