挑战程序设计竞赛3.1习题:Moo University - Financial Aid POJ - 2010

(原题见POJ2010)

这道题我之前采用了优先队列+预处理的方法求解(https://www.cnblogs.com/jacobfun/p/12244509.html),现在用二分的办法进行求解。

一开始我很纳闷,采用二分求解本题,如果二分的mid值不符合条件,按照二分右边界应该为mid - 1(我采用前闭后闭的二分),那么如果mid + xxx(xxx大于0)可以呢?(考虑mid不行是因为左边最小加起来大了,mid ~ mid + xxx中有极小值,使得mid + xxx的左边可以满足,那么这样子就矛盾了,怎么能二分呢?(百思不得其姐))后来我仔细看了网上的博客,终于发现了题解与我的想法不同之处——不是不满足条件一定缩小右边界!!!

这道题是这样二分的:先对于分数从小到大排序,我们先在n/2~c - n/2的范围内(因为中位数只能存在于这个范围内,n为录取的奶牛,c为申请的奶牛)取mid,判断是否有在mid左右边各存在n/2个数使得mid的资助费+左右的资助费<=最多拿来做助学金的费用f,我们找出当总助学金小于等于f且中位数是mid时,左右最多有多少个(当然左右的个数不能超过n/2),有以下几种情况:

1.如果左右最大存在的使得总资助费小于f的个数均小于n/2,换言之mid左右都不可能存在有n/2个满足总资助费小于f的,那么不管你把值变成mid的左边(那样因为二分搜索的值在mid左边,那么左边可选的总数(也就是分数比搜索的值小的)是我们之前搜索mid的左边的子集(现在搜索的值的左边分数 < 现在搜索的值的分数 < mid, 所以现在搜索的值的左边的分数集合是mid的左边分数集合的子集),之前mid左边分数的集合都没有满足题意的情况,它的子集更不可能),或者把值变成右边(同左边解释一样),都不可能,所以答案无解直接输出-1

2.如果只满足某一项,比如只有mid左边个数小于n/2,那么范围在(left ~ mid)之间他们的每个值的左边都是mid的左边的子集(而mid的左子集不行),所以也都不行,只能把范围变成mid + 1 ~ right,当只有mid右边的个数小于n/2时,在(mid ~ right)之间每个值的右边的子集都是mid的右边的子集,也都不行,范围就是left ~ mid - 1。

3.如果都满足n/2,那么这就是某一可能值,我们要记录下来。

AC代码:

#include <stdio.h>
#include <algorithm>
using namespace std;
struct Node{
    int score;
    int need;
    friend bool operator <(Node x, Node y){
        return x.score < y.score;
    }
}stu[100005];
struct Nodes{
    int score;
    int need;
    int id;
    friend bool operator <(Nodes x, Nodes y){
        return x.need < y.need;
    }
}stu2[100005];//为了找到是否存在我们对mid的左右采取取符合条件的最小值,所以要对助学金进行排序,从小到大,这样每次找到的一定是满足条件最小的,如果这样都不能找到符合题意的,那么说明不存在这样的解
int ans;//答案
int n, c, f;
int check(int x)
{
    int sum = stu[x].need, l = 0, r = 0;//注意计算总助学金sum别忘了中位数的助学金,l是x(也就是mid)的左边满足sum <= f的最大个数
    for(int i = 0; i < c; i++)
    {
        if(stu2[i].id < x && stu2[i].need + sum <= f && l < n / 2)//如果这个值使得sum加上后小于等于f,且在x的左边,且左边的值现在还没找到n/2
        {
            l++;
            sum += stu2[i].need;
        }
        else if(stu2[i].id > x && stu2[i].need + sum <= f && r < n / 2)
        {
            r++;
            sum += stu2[i].need;
        }
    }
    if(l < n / 2 && r < n / 2)//情况1
        return -1;
    else if(l < n / 2)//情况2
        return 1;
    else if(r < n / 2)
        return 2;
    else//符合题意的解
        return 0;
}
int main(void)
{

    scanf("%d %d %d", &n, &c, &f);
    for(int i = 0; i < c; i++)
    {
        scanf("%d %d", &stu[i].score, &stu[i].need);
    }
    sort(stu, stu + c);
    for(int i = 0; i < c; i++)
    {
        stu2[i].id = i;//记录对分数排序的情况下的编号,便于区分是mid的左边还是右边,对于分数相同的,我们也以不同编号进行处理,这样可以考虑到中位数值相同的情况
        stu2[i].need = stu[i].need;
        stu2[i].score = stu[i].score;
    }
    sort(stu2, stu2 + c);//对助学金进行排序
    int left = n / 2, right = c - n / 2;//前闭后闭
    while(left <= right)
    {
        int mid = (left + right) / 2;
        int c = check(mid);
        if(c == -1)
        {
            ans = -1;
            break;
        }
        else if(c == 1)
        {
            left = mid + 1;
        }
        else if(c == 2)
        {
            right = mid - 1;
        }
        else
        {
            ans = stu[mid].score;
            left = mid + 1;
        }
    }
    printf("%d\n", ans);
    return 0;
}

  

原文地址:https://www.cnblogs.com/jacobfun/p/12357492.html

时间: 2024-08-02 12:01:06

挑战程序设计竞赛3.1习题:Moo University - Financial Aid POJ - 2010的相关文章

Moo University - Financial Aid (poj 2010 优先队列 或者 二分)

Language: Default Moo University - Financial Aid Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 5551   Accepted: 1663 Description Bessie noted that although humans have many universities they can attend, cows have none. To remedy this p

挑战程序设计竞赛2.4习题:Moo University - Financial Aid POJ - 2010

Bessie noted that although humans have many universities they can attend, cows have none. To remedy this problem, she and her fellow cows formed a new university called The University of Wisconsin-Farmside,"Moo U" for short. Not wishing to admit

Moo University - Financial Aid POJ 2010(枚举)

原题 题目链接 题目分析 题目要求取中位数,我们可以枚举以每个牛的分数作为中位数,然后选出最省钱的方案,最后只需要看花钱数不超过F中的最高的中位数分数即可.说一下具体实现,先对所有牛按分数从小到大排序,选出最省钱的方案可以用优先队列来选,设half=n/2,当优先队列的牛超过half个的时候就把一个花费最多的弹出去,这样从小到大遍历一遍就可以记录分数比中位数小的那些牛的花费,记为lower[i],从大到小遍历一遍就可以记录分数比中位数大的那些牛的花费,记为upper[i],最后这个方案的总花费就

挑战程序设计竞赛2.3习题:Making the Grade POJ - 3666

A straight dirt road connects two fields on FJ's farm, but it changes elevation more than FJ would like. His cows do not mind climbing up or down a single slope, but they are not fond of an alternating succession of hills and valleys. FJ would like t

poj2010 Moo University - Financial Aid

Moo University - Financial Aid 题意: 一个私立学校的学生要申请奖学金,而学校的金额有限.因此,学校希望在金额不超过F的情况下从C中选得N对数. 给出三个数N,C,F.分别代表在C对数中要取得N对数. 而每对数分别代表成绩,跟申请金额.要求取得N对数中的总金额不超过F的条件下,然取得中间的以为学生的成绩最高.(N为even) 算法分析: 本题有两种解法,一种是用优先队列,一种是二分; 一.利用堆实现 先说堆的实现方法,我们能够现对头尾的N/2进行处理,由于头尾的N/

POJ 2010 Moo University - Financial Aid( 优先队列+二分查找)

POJ 2010 Moo University - Financial Aid 题目大意,从C头申请读书的牛中选出N头,这N头牛的需要的额外学费之和不能超过F,并且要使得这N头牛的中位数最大.若不存在,则输出-1(一开始因为没看见这个,wa了几次). 这个题的第一种做法就是用两个优先队列+贪心. /* * Created: 2016年03月27日 14时41分47秒 星期日 * Author: Akrusher * */ #include <cstdio> #include <cstdl

POJ2010 Moo University - Financial Aid(二分法)

题目地址 分析:如果用二分法,关键是score和aid分开排序,score排序是为了充分利用中位数的性质,这样就可以确定m左右必须各选N/2个,到这之后有人是用dp求最优解,可以再次按照aid排序一次,可以直接确定最优解(肯定是从最小的开始选择!): 1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 using namespace std; 5 int N, C, F; 6 const i

poj 2010 Moo University - Financial Aid(优先队列(最小堆)+ 贪心 + 枚举)

Description Bessie noted that although humans have many universities they can attend, cows have none. To remedy this problem, she and her fellow cows formed a new university called The University of Wisconsin-Farmside,"Moo U" for short. Not wish

poj2010(Moo University - Financial Aid)优先队列

Description Bessie noted that although humans have many universities they can attend, cows have none. To remedy this problem, she and her fellow cows formed a new university called The University of Wisconsin-Farmside,"Moo U" for short. Not wish