贪心+二分+快速排序

贪心思想:比如你要做个贪婪的人,但是你要拿的东西有数量限制,你是不是就是从最好的东西开始拿。
贪心满足局部最优解,不一定满足全局最优解

二分查找的常见应用
①:假定一个解,并判断是否可行。
在这里插入图片描述
和下面的分蛋糕的题一样。就是一个一个试。
②:最大化最小值。
在这里插入图片描述
我们假设每头牛之间的最短距离为k,那么此时我们判断在满足最短距离为k的情况下是否能用牛舍将牛都放完,如果可以则增大查找范围,否则减小查找范围。
③:最大化平均值。
有n个物品的重量和价值分别是 W[i]和 V[i], 从中选出k个物品使得单位重量的价值最大。
限制条件: 1 ≤ k ≤ n ≤ 10000
1 ≤ w[i], v[i], ≤ 1000000
二分确定解,我们假设C(x)为可以选择使得单位重量的价值不小于x。由此,问题就变为了可以满足C(x)的最大x。

那么我们如何去判断一个x是否可行呢?我们假设我们选定了某个物品集合S
,那么他们的单位重量的价值为 在这里插入图片描述
因此就变成了判断是否存在S满足下面的条件 在这里插入图片描述
,将这个不等式变形之后就得到 。

在这里插入图片描述
因此就可以对(v[i]-x.w[i]) ≥ 0的值进行排序贪心的进行选取。
在这里插入图片描述

问题 A: 这些卡片非常OK

题目描述
现在有一些写着数字的卡片,我们定义一个规则:
两张数字相同的卡片是 OK 的,
三张连续数字的卡片是 OK 的,
给你一堆卡片,请你找出尽可能多的 OK 数,卡片不可以重复利用。
输入
多组输入,每组输入第一行为一个整数 n 表示卡片数量,1 ≤ n ≤ 1000000, n = 0 时输入结束。
接下来一行为 n 张卡片上的数字 x ,可能乱序。1 ≤ x ≤ 100 。
输出
输出卡片能凑出最多 OK 的数量。
样例输入
4
2 2 3 3
5
1 2 3 4 4
0
样例输出
2
2
提示
如样例一,(2 2) 为一个 OK ,(3 3) 为一个 OK,所以输出为 2

贪心思想:肯定是让两个牌的ok数最多,然后再在剩下的牌里找三个
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000000+5;
int kapian[maxn];
vector<int> v;
int solve(int a[maxn],int n){
  sort(a,a+n);  //先排序
  int flag=0;  //flag为ok数
  for(int i=0;i<n;i++){
    if(a[i]==a[i+1]&&(i+1<n)){  //找两两的
      flag++;
      a[i]=a[i+1]=0;  //将他赋值为0,因为你将两两的赋值为0,再一排序两两的0不就跑到前面了,然后再在最后一个0以后找三张连续牌
    }
  }
  sort(a,a+n);//再排序
  int i;
  for( i=0;a[i]==0;i++){  //找到0
      for(;i<n;i++){
         if((a[i+1]==a[i]+1)&&(a[i+2]==a[i]+2)&&(i+3<n)){
           flag++;
           i+=2; //每次位置跳2,但是后面又有个i++,所以这里只需+2
         }
      }
  }
  return flag;//返回总数
}
 int main(){
   int n;
   while(scanf("%d",&n)&&n){
        for(int i=0;i<n;i++){
              scanf("%d",&kapian[i]);
        }
        cout<<solve(kapian,n)<<endl;
   }
 }

问题 B: 佳星的苹果派

题目描述
佳星最近开了一家专营苹果派的店,开店以来生意火爆,所以他打算扩大规模。为了做好扩大规模的准备工作,他进了一大批的苹果。
但是扩大规模后发现他进的苹果太多了,一时间卖不完,但是果农现在已经没有苹果了,也就是说,接下来他只能靠剩下的苹果过活,但是周期问题导致苹果对苹果派的味道加成不一样。
苹果派的味道即苹果味道加成总和, 即 如果三个苹果加成的味道分别为 1, -1, 3 那苹果派最后的味道为3
但是今天客户下单的苹果派他不小心把苹果拿出来拿多了,所以他需要拿掉一部分苹果,但是他希望尽可能的保持苹果派的味道变化小,所以现在需要你编写一个程序来计算苹果派的味道问题。如果变化程度相同,比如去掉一个苹果有, 苹果派的味道原来是 12, 有一个苹果是1, 另一个苹果是 -1, 则选择去掉-1那个,苹果派味道变成13,即使得苹果派的味道在变化最小的情况下尽可能变得味道更好, 当然,优先变化最小。

输入
有多组输入样例
第一行包括一个n ,m ( 0< m < n <=100000) n 是苹果总数,m是需要拿掉的苹果数量
第二行有n 个数字,表示每个苹果对苹果派的味道加成( -100<= a[i] <= 100)
输出
输出拿掉m个苹果后味道变化最小的味道总和
样例输入
3 1
-1 0 10
5 2
2 2 1 -1 0
样例输出
9
5
提示
绝对值相等的时候选择拿掉加成为负的那个

贪心思想:我只需在排序时对这个排序操作进行一下改变,把我想去掉的苹果排到前面
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100000+7;
int n,m;
int a[maxn];

int cmp(int x,int y){   //重载判断条件
  if(abs(x)==abs(y)){  //绝对值相等,把小的放到前面,比如-1,1,因为我要尽可能的让他的口味变化最小,在这前提下再让口味最优,
    return x<y;
  }else
    return abs(x)<abs(y);  //否则将绝对值小的放前面,比如-3,2,因为我要尽可能的让他的口味变化最小。
}

int main(){
  while(cin>>n>>m){
    int sum = 0;
    for(int i = 0;i<n;i++){
      cin>>a[i];
      sum += a[i];
    }
    sort(a,a+n,cmp);  //在之前的条件下排序
    for(int i = 0;i<m;i++){
      sum -= a[i];
    }
    cout<<sum<<endl;
  }
  return 0;
}

问题 D: 数字游戏

时间限制: 1 Sec 内存限制: 128 MB

题目描述
给你一个长度为N的数组A,数组元素为N的一个排列。你的任务是使用数组中的数字凑出M,每个数字只能使用一次。
输入
输入多组数据
输入第一行,两个数字,N, M, 1 <= N <= 1000000, 1< =M <= 100000000。
接下来是长度为N的数组, 1 <= A[i] < =1000000。保证所有数字的个数和不超过1000000。
输出
输出一行,如果能用长度为N的数组凑出M输出“YES”,否则输出"NO" ,输出不包括引号。
样例输入
3 6
1 3 2
4 3
1 4 3 2
样例输出
YES
YES

#include<bits/stdc++.h>
using namespace std;
const int maxn=1000000+5;
int a[maxn];
int main(){
       int n,m;
       while(cin>>n>>m){
         for(int i=0;i<n;i++)
            cin>>a[i];
          sort(a,a+n);
          for(int i=n-1;i>=0;i--){
            if(m>=a[i]){
                m-=a[i];
            }
            if(m<=0){
                break;
            }
          }
          if(m==0)
          cout<<"YES"<<endl;
          else
          cout<<"NO"<<endl; 
       }
}

问题 E: 分批萨

时间限制: 1 Sec 内存限制: 128 MB
题目描述
红红马上过生日了,他打算买n块蛋糕用来庆祝生日,假设n块蛋糕都为圆柱形,他们的底面圆的半径不一定相等但是他们的高都相等且为1,他邀请了F个人参加他的生日Party,保证每个人都能来,现在他想把这n块蛋糕等体积的分给包括他的参加聚会的所有人,为了保证蛋糕的颜值,他想每块蛋糕只能被切开但是不能重新组合,现在问你每个人最大能分到多大的蛋糕, 结果保留至小数点后四位。
输入
输入第一行为一个整数t,表示样例数。
对于每个样例,输入第一行包括两个正整数n,F分别表示红红打算买蛋糕的数目以及红红邀请的客人数。
数据保证(1 ≤ n,F ≤ 10000)。每组测试样例的第二行包括n个正整数,分别表示n块蛋糕的半径r,每两个整数之间有一个空格。(1 ≤ r ≤ 10000)。
输出
对于每组测试样例,请输出每个人能分到的蛋糕的最大体积,回答为一个浮点数,精确到小数点后四位。
样例输入
3
3 3
4 3 3
1 24
5
10 5
1 4 2 3 4 5 6 5 4 2
样例输出
25.1327
3.1416
50.2655

二分思想:用二分去试我分后的体积,在最小的区间和最大的区间进行二分,然后进行判断。
#include<bits/stdc++.h>
using namespace std;
const int maxn=10000+5;
const double pi=acos(-1.0);
double s[maxn];
int n,f;
bool check(double x ){  //x为分后半径
   double ss=pi*x*x;   //ss为分后体积,也是面积,因为高为1
   int sum=0; //sum为能分到的块
   for(int i=0;i<n;i++){
    sum+=(int)(s[i]/ss);   //对每一块进行试,看他在分后体积为ss的情况下,能分多少块
   }
   return sum>=(f+1);   
   //如果我在分后体积ss的情况下,分到的块数大于我要给的人了,说明我分后体积给小了,如果小于,说明我分后体积给小了。
}
void slove(){
  double min=0,max=10000,mid;  //题目给出最大半径为10000,所以我分后的体积的半径最大肯定也是10000,所以我在0到10000的区间二分
  for(int i=0;i<100;i++){  
        mid=(min+max)/2;
        if(check(mid)) {
              min=mid;
        }
        else{
              max=mid;
        }
    }
    printf("%.4lf\n",max*max*pi);
}

int main(){
   int t;
   cin>>t;
   while(t--){
        while(cin>>n>>f){
             double r;
             memset(s,0,sizeof(s));
             for(int i=0;i<n;i++){
               cin>>r;
               s[i]=pi*r*r;
             }
             slove();
        }
   }
}

问题 F: 寻找第k大的数

时间限制: 1 Sec 内存限制: 128 MB
题目描述
给出n个无序的整数,请输出其中按升序排列后的第k个数。(请应用分治(快排)思想)
输入
有多组测试样例,每组测试样例的第一行包含两个正整数n,k(1 ≤ k ≤ n ≤ 10000000)。
第二行包含n个整数,每两个数之间由一个空格分开。保证每个数的绝对值都小于1000000。
输出
对于每组测试样例,输出一个整数,表示按升序排列后的第k个数。
样例输入
5 3
1 4 2 5 6
5 5
1 10 2 3 4
样例输出
4
10

这题要求快速排序,用的二分思想
#include<stdio.h>
#include<stdlib.h>
int a[10000000];

int partition(int low, int high) {
	int pivotkey = a[low];
	while (low < high) {
		while (low < high&&a[high] >= pivotkey)--high;
		a[low] = a[high];
		while (low < high&&a[low] <= pivotkey)++low;
		a[high] = a[low];
	}
	a[low] = pivotkey;
	return low;
}

void soft(int low, int high) {
	int pivotloc;
	if (low < high) {
		pivotloc = partition(low, high);
		soft(low, pivotloc - 1);
		soft(pivotloc + 1, high);
	}
}
int main() {
	int n,k;
	while(scanf("%d %d", &n,&k)!=EOF){
			for (int i = 0; i <n; i++)
					scanf("%d", &a[i]);
			soft(0, n-1);
			printf("%d\n", a[k-1]);
	}
	return 0;
}

问题 G: 码农红红

时间限制: 1 Sec 内存限制: 128 MB
题目描述
红红准备了m斤肉肉准备和肉店兑换金钱以供自己上网码代码,现在有n家肉店,第i家肉店最多需要J[i]斤肉肉,如果全部交换红红可以获得F[i]美金,当然红红可以选择在某家肉店只交换一部分,红红问你他最多能获得多少美金。
输入
输入第一行包含两个正整数m,n分别表示红红初始拥有的肉肉的斤数和肉店数。
接下来n行,每行包括两个正整数分别表示第 i 家肉店的F[i] 和 J[i],意思如题目所述,保证所有整数都小于1000.
输出
输出一个浮点数,表示红红能获得的最大美金数,保留小数点后三位。
样例输入
5 3
7 2
4 3
5 2
样例输出
13.333

这题是个贪心题:我肯定先去单位肉价格最大的店,然后再去次之的店,直到我把手上的肉全交换完,所以我就根据这一条件排序
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000;
struct node{
    double money,jin;
    double xjb;
}y[maxn];
bool operator < (node a,node b){  //重载运算符,也可以在sort函数参数里定义函数,去改变运算符条件
    if(a.xjb>b.xjb) return true;
    else return false;
}
int m,n;  //m初始斤数  n家店
void solve(){
   sort(y,y+n);
   double sum=0; //能换到的总钱数
   for(int i=0;i<n;i++){
      if(y[i].jin<=m){  //我手上的肉大于这家店的总需求肉
            sum+=y[i].money;
            m-=y[i].jin;
        }
        else{   //我手上的肉小于这家店的总需求肉,那就把我能给的都给了
          sum+=y[i].xjb*m;  
          break;
        }
   }
   printf("%.3lf\n",sum);
}

int main(){
while(cin>>m>>n){
       for(int i=0;i<n;i++){
          cin>>y[i].money>>y[i].jin;
          y[i].xjb=(y[i].money)/(y[i].jin);  //int除Int还是个int,所以用double除double
     }
     solve();
   }
   return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值