CodeForces 609 D.Gadgets for dollars and pounds(二分+贪心)

本文提出了一种算法,通过处理汇率序列并结合物品价格信息,来确定使用何种货币购买指定数量的物品,以达到最少天数完成购买的目标。算法利用了前缀最小值、排序和二分搜索等技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description
给出n天内每天美元和英镑对一种特殊货币burles的汇率,有m件物品,每种物品只能用美元或者英镑买,一个人有s个burles,她不能存着美元或者英镑,只能在买东西的时候再兑换,且每种物品只能买一次,问这个人想买k件物品最少需要多少天
Input
第一行四个整数n,m,k,s分别表示天数,物品数,要买的物品数和钱数,之后n个整数a[i]表示第i天一美元值多少burles,再有n个整数b[i]表示第i天一英镑值多少burles,最后m行每行两个整数ti和ci表示第i种物品能用哪种货币买(1表示美元2表示英镑),ci表示买该物品的花费
(1<=n<=2e5,1<=k<=m<=2e5,1<=s<=1e9,1<=ai,bi<=1e6,1<=ci<=1e6)
Output
如果n天内可以买到k件物品则输出最少天数以及要买的k件物品编号以及买这件物品的时间,否则输出-1
Sample Input
5 4 2 2
1 2 3 2 1
3 2 1 2 3
1 1
2 1
1 2
2 2
Sample Output
3
1 1
2 3
Solution
预处理a和b序列的前缀最小值ma[i]和mb[i]表示要在汇率最小时买才能使得burles最有价值,把两种货币能买的物品分别按价值升序排后求一个前缀和sb[i]和sp[i]表示每次买最便宜的物品,二分天数,对于一个二分值mid,枚举买能够用第一种货币买的物品的数量x,那么用第二种货币要买k-x个物品,维护ma[mid]*sb[x]+mb[mid]*sp[k-x]的最小值,如果最小值不超过s说明二分值合法,缩小二分值找更优的解,否则二分值过小,移动区间左端点判更大的二分值是否合法
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define maxn 222222
int n,m,k,s,n1,n2,x;
int a[maxn],b[maxn],ma[maxn],mb[maxn];
struct node
{
    int c,id;
    bool operator<(const node&b)const
    {
        return c<b.c;
    }
}d[maxn],p[maxn];
ll sd[maxn],sp[maxn];
bool check(int mid)
{
    int aa=a[ma[mid]],bb=b[mb[mid]],t=-1;
    for(int i=0;i<=k;i++)
        if(i<=n1&&k-i<=n2)
        {
            if(t==-1||sd[i]*aa+sp[k-i]*bb<sd[t]*aa+sp[k-t]*bb)
                t=i;
        }
    if(sd[t]*aa+sp[k-t]*bb<=s)
    {
        x=t;
        return 1;
    }
    return 0;
}
int main()
{
    while(~scanf("%d%d%d%d",&n,&m,&k,&s))
    {
        n1=n2=0;
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)scanf("%d",&b[i]);
        int temp=1;
        for(int i=1;i<=n;i++)
            if(a[i]>=a[temp])ma[i]=temp;
            else ma[i]=temp=i;
        temp=1;
        for(int i=1;i<=n;i++)
            if(b[i]>=b[temp])mb[i]=temp;
            else mb[i]=temp=i;
        for(int i=1;i<=m;i++)
        {
            int type,c;
            scanf("%d%d",&type,&c);
            if(type==1)d[++n1].c=c,d[n1].id=i;
            else p[++n2].c=c,p[n2].id=i;
        }
        sort(d+1,d+n1+1),sort(p+1,p+n2+1);
        sd[0]=sp[0]=0;
        for(int i=1;i<=n1;i++)sd[i]=sd[i-1]+d[i].c;
        for(int i=1;i<=n2;i++)sp[i]=sp[i-1]+p[i].c;
        int l=1,r=n+1,mid,ans=n+1;
        while(l<r)
        {
            mid=(l+r)/2;
            if(check(mid))ans=mid,r=mid;
            else l=mid+1; 
        }   
        if(ans==n+1)printf("-1\n");
        else
        {
            printf("%d\n",ans);
            for(int i=1;i<=x;i++)printf("%d %d\n",d[i].id,ma[ans]);
            for(int i=1;i<=k-x;i++)printf("%d %d\n",p[i].id,mb[ans]);
        }
    } 
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值