C. Save the Nature(二分+贪心)

在这里插入图片描述在这里插入图片描述
这道题首先应该理解题意:
题目给你n个商品的价格(并且各自的商品价格对于各自的坐标从[1,n])说叫你自己去选择一个顺序,并且给你两个条件:
1.给你x,a,x表示在位置a,2a,3a,4a…对应的商品价格为pix%;
2.给你y,b,y表示在位置b,2b,3b…对应的商品价格为pi
y%;
3.如果a,b的公倍数的位置,那么对应的商品价格为pi*(x+y)%;
4.其他位置对应商品价格为pi0;
然后就是这个k的含义了;
k表示从下标1----k的位置对应的商品价格的总和就应该>=k,那么这个k才是满足条件的;
然后让我们求最小的k;如果没有那么就输出-1;
好了,这道题就这样理解到了;
然后怎么写呢?
首先肯定要输入原价格;因为有一个%,所以为了避免浮点型,所以我就直接用ll了
然后在和k比较时,我就只需要比较k
100;
这样读完题之后,应该可以想到,对应的位置的价格不应该是越大越好吗?
所以每次我都去找1–k位置的和,并且把在位置[1,k]的商品尽量放在更大的对应的max(x,max(y,x+y))上,所以这样就能使得尽可能大的价格被出售;
关键是我怎么去定这个k呢?
因为一共只有n个位置,所以我只需要二分查找个k就可以了;
如果求出来的前mid个的sum>=k那么就表明这个k是存在的;那么就r=mid-1;
如果求出来的前mid个的sum<k,那么就表明这个k需要往上扩大,才能使得更多的商品被加进来;
所以二分的思想就是这样滴;
AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long  ll;
vector<ll> q1;
vector<ll> q2;
ll n,x,y,a,b,k;//全局变量
ll l=1,r=n;
ll ans=0;
ll mid;
ll gcd(ll a,ll b){
   return b?gcd(b,a%b):a;
}
bool cmp(ll a,ll b){return a>b;}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
       q1.clear();
        scanf("%lld",&n);
        for(int i=1;i<=n;i++){
            ll t;
            scanf("%lld",&t);
            q1.push_back(t);//避免精度
        }
        scanf("%lld %lld %lld %lld %lld",&x,&a,&y,&b,&k);
        ll gc=gcd(a,b);//求最大公约数
        ll lcm=a*b/gc;//求他们的最小公倍数,如果是lcm的倍数,那么肯定就是a,b的倍数
       sort(q1.begin(),q1.end(),cmp);//排序
       l=1,r=n;//二分区间
        ans=0;
       while(l<=r){
                 q2.clear();
            mid=(l+r)/2;
            for(int j=1;j<=mid;j++){//找[1,mid]区间的对应位置的比例价格值
             if(j%lcm==0){
                q2.push_back(x+y);
             }else if(j%a==0){
                q2.push_back(x);
             }else if(j%b==0){
                q2.push_back(y);
              }
            }

            ll res=0;
            sort(q2.begin(),q2.end(),cmp);
            for(int i=0;i<q2.size();i++){//这里就用了贪心思想,把对应max(x,max(y,x+y))*对应价格对大值,因为题目说了可以任意排序
                res+=q1[i]*q2[i];
            }
           if(res>=k*100){//如果比k*100大,那么就要缩小右区间
              r=mid-1;
              ans=mid;//记录答案
           }else{
              l=mid+1;
           }

       }
//       cout<<"f"<<endl;
       if(!ans)printf("-1\n");//判断是否ans存在
       else printf("%lld\n",ans);
    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值