CF 332 C 贪心

本文探讨了一个复杂的决策问题,即在有限的资源约束下,如何最优地选择命令并进行决策。文章详细介绍了问题背景、算法设计思路及实现步骤,并通过实例演示了解决方案的应用。主要关注于最大化主席的个人利益(最小化头发变灰数量),同时考虑议员的满意度(最小化不高兴值)。通过贪心算法,文章提出了一个高效的解决方案,确保在满足所有约束条件的前提下,达到最优决策效果。
题目链接:http://codeforces.com/problemset/problem/332/C


参考  链接: http://blog.youkuaiyun.com/cc_again/article/details/9471465


题目意思:


有n个命令,要通过p个,某主席要在通过的p个中选择k个接受。


每个任务有两个值ai,bi, ai表示如果该主席接受该命令,她的头发变灰的数量,bi表示如果该主席不接受该命令时,议员不高兴值。


对于通过的p个命令,该主席要使议员的不高兴值和最小,在相同的情况下,要使自己的头发变灰的数量尽可能的少。


让你求出通过哪p个命令,使得该主席的头发变灰的数量最多,在相同的情况下,输出使议员不高兴最大的选择。



解题思路:

                    非常关键的一点是主席的想法和我们不同,我们先选出主席一定不会通过的p-k条命令来,那么这p-k条命令必须

unhappy最小,然后那k条必须选的选择num(白头发数量)最多的,这样头发多的选出来了,再更新那不选的p-k条,大致思路

说完了,看代码吧:




/*
题目意思:

有n个命令,要通过p个,某主席要在通过的p个中选择k个接受。

每个任务有两个值ai,bi, ai表示如果该主席接受该命令,她的头发变灰的数量,bi表示如果该主席不接受该命令时,议员不高兴值。

对于通过的p个命令,该主席要使议员的不高兴值和最小,在相同的情况下,要使自己的头发变灰的数量尽可能的少。

让你求出通过哪p个命令,使得该主席的头发变灰的数量最多,在相同的情况下,输出使议员不高兴最大的选择。

*/


//   好难的贪心

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define N 101000

struct stud{
int num,unhappy;
int pos,pp;
}f[N],s[N];

int n,p,k,vis[N];
int ans[N];

int cmp1(stud a,stud b)  //按照主席的习惯,先选不高兴多,然后掉头发也少的
{
    if(a.unhappy==b.unhappy)
        return a.num<b.num;

    return a.unhappy>b.unhappy;
}

int cmp2(stud a,stud b)  //按照我们意愿,选头发掉的多,为什么unhappy大的在前面呢,请看cmp1,unhappy大的在前面,
                          //我们找到前面的unhappy最小的越大,后面掉头发的unhappy的选择越大,
{
     if(a.num==b.num)
       return a.unhappy>b.unhappy;

    return a.num>b.num;
}

int main()
{
    int i;
    while(~scanf("%d%d%d",&n,&p,&k))
    {

       for(i=1;i<=n;i++)
       {
           scanf("%d%d",&f[i].num,&f[i].unhappy);
           f[i].pos=i;
       }

       sort(f+1,f+n+1,cmp1);//按不高兴减序,白头发增序

       memcpy(s,f,sizeof(f));  //请记住,一定要开另一个结构体,不要像我下面的一样,因为cmp2排序后再cmp1可能不是这个序列了

       for(i=1;i<=n;i++)
        f[i].pp=i;

       sort(f+1,f+n-(p-k)+1,cmp2);  //留下最少逼迫主席选前面num大的p-k个(暂时逼迫主席的,后面会更新)

        int j=1;

        for(i=1;i<=k;i++)
        {
            j=max(j,f[i].pp);

            if(i==1)
                printf("%d",f[i].pos);
            else
                printf(" %d",f[i].pos);
        }

        sort(s+j+1,s+n+1,cmp1);

        for(i=j+1;i<=j+p-k;i++)
            printf(" %d",s[i].pos);

        printf("\n");
    }
    return 0;
}


//附上一份让我自己写的吐血的错误代码
   
   //就是没有开两个数组,wa到死

/*

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100005

struct stud{
int hair,anger;
int pos,pp;
}f[N];


int n,p,k;

int cmp1(stud a,stud b)
{
    if(a.anger==b.anger)
        return a.hair<b.hair;

    return a.anger>b.anger;
}

int cmp2(stud a,stud b)
{
      if(a.hair==b.hair)
            return a.anger>b.anger;

      return a.hair>b.hair;
}

int main()
{
    int n,i;
    while(~scanf("%d%d%d",&n,&p,&k))
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&f[i].hair,&f[i].anger);
            f[i].pos=i;
        }

        sort(f+1,f+n+1,cmp1);

        for(i=1;i<=n;i++)
            f[i].pp=i;

        sort(f+1,f+n+1-(p-k),cmp2);

        int j=1;

        for(i=1;i<=k;i++)
           {
            if(i==1)
               printf("%d",f[i].pos);
            else
               printf(" %d",f[i].pos);

            j=max(j,f[i].pp);
           }


         sort(f+1,f+n+1,cmp1);

         sort(f+j+1,f+n+1,cmp1);

         for(i=j+1;i<=j+p-k;i++)
            printf(" %d",f[i].pos);

         printf("\n");
    }

    return 0;
}


*/




<think>嗯,用户想找Codeforces 1380C题目的解法。这道题在Codeforces上属于常规比赛题目,考察的主要是贪心算法和排序技巧。 用户可能是正在备赛的选手,需要快速理解题目解法。从问题表述看,用户直接要solution,说明ta更关注实现而非题目描述,因此回复要精简,核心是算法思路和代码实现。 这道题的关键点在于:题目要求将数组分成若干子组,每个子组满足"组内最小值×组大小≥给定值x"。最优策略是倒序排序后,用贪心法尽可能多地形成大组,因为小数在末尾不会拉低大组的乘积。 准备用标准解法:先排序,然后从最大值开始遍历,动态维护当前组的大小和最小值。当满足条件时立即分组。这样时间复杂度是O(n log n),主要来自排序。 需要特别注意边界情况:当所有元素都小于x时,可能每个元素单独成组;当x很大时,可能无法分组。这些在代码中要处理。 用户可能还会问时间复杂度的证明或贪心正确性,所以相关问题可以设计这两个方向。另外考虑到实际编码常见错误,准备添加一个关于"常见错误"的问题提示。</think>以下是Codeforces 1380C(Create The Teams)的题解,基于贪心算法实现: ### 题目核心 给定长度为 $n$ 的数组 $a$ 和整数 $x$,需将数组分成若干子组,使得每组满足: $$ \text{组内最小值} \times \text{组大小} \geq x $$ 求最大分组数量。 ### 贪心策略 1. **排序与倒序遍历** - 将数组 **降序排序**(从大到小),使较大元素优先成组 - 倒序遍历可避免小数拉低组内最小值 2. **动态维护分组** - 用 $current\_min$ 记录当前组的最小值(即最新加入的元素) - 用 $count$ 记录当前组大小 - 当满足 $current\_min \times count \geq x$ 时立即分组 ### 代码实现 ```python t = int(input().strip()) results = [] for _ in range(t): n, x = map(int, input().split()) a = list(map(int, input().split())) a.sort(reverse=True) # 降序排序 groups = 0 current_min = float('inf') count = 0 for num in a: current_min = min(current_min, num) # 更新组内最小值 count += 1 # 组大小+1 if current_min * count >= x: # 满足条件时成组 groups += 1 current_min = float('inf') count = 0 results.append(groups) for res in results: print(res) ``` ### 算法正确性证明 - **贪心选择有效性**:较大元素优先成组可最大化利用其值(因 $current\_min$ 由最小元素决定,大元素不会降低该值)[^1] - **时间效率**:排序 $O(n \log n)$ + 遍历 $O(n)$,总复杂度 $O(n \log n)$ ### 示例分析 输入:`a = [2, 5, 3, 1], x = 6` 1. 降序排序:`[5, 3, 2, 1]` 2. 遍历过程: - 第一组:`[5]` → $5 \times 1 = 5 < 6$(继续) `[5,3]` → $\min(5,3) \times 2 = 3 \times 2 = 6 \geq 6$ → 成组 - 第二组:`[2]` → $2 \times 1 = 2 < 6$(继续) `[2,1]` → $\min(2,1) \times 2 = 1 \times 2 = 2 < 6$ → 无法成组 3. 输出:`1`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值