洛谷 P2376 [USACO09OCT]津贴Allowance

洛谷 P2376 [USACO09OCT]津贴Allowance


题目

题目背景

作为学习刻苦、成绩优秀的回报,妈妈决定开始每个星期给杀马特一点零花钱。

题目描述

作为创造产奶纪录的回报,Farmer John决定开始每个星期给Bessie一点零花钱。

FJ有一些硬币,一共有N (1 <= N <= 20)种不同的面额。每一个面额都能整除所有比它大的面额。

他想用给定的硬币的集合,每个星期至少给Bessie某个零花钱的数目C (1 <= C <= 100000000)。请帮他计算他最多能支付多少个星期的零花钱。

输入输出格式

输入格式:
第1行: 两个由空格隔开的整数: N 和 C;

第2到第N+1行: 每一行有两个整数表示一个面额的硬币:硬币面额V (1 <= V <= 100,000,000)拥有的该面额的硬币数B (1 <= B <= 1,000,000)。

(感谢fyszzhouzj,现错误已改正)

输出格式:
第1行: 一个单独的整数,表示最多能给支付多少个星期至少为C的零用钱。

输入输出样例

输入样例#1:

3 6
10 1
1 100
5 120

输出样例#1:

111

说明

【样例说明】

杀马特的妈妈想要每个星期给杀马特六美分。他有100个1美分硬币,120个5美分硬币,和一个10美分硬币。他妈妈可以在一个星期超额付给杀马特一个10美分硬币,然后接下来的10个星期每星期付给杀马特两个5美分硬币。最后100个星期每星期付给杀马特一个1美分硬币跟一个5美分硬币。


题解

贪心

先把面额超过或等于 C (我的代码里面是m)的直接当做一个星期的零花钱,其余的加入数组,按面额排序

我先将一个星期的零花钱尽量用大面额的钱填充(即从大面额到小面额的钱来填充),如果还有剩余,那么就用1张剩余的最小面额的钱填充即可(因为题目上说过每一个面额都能整除所有比它大的面额,如果还有剩余的话,最小面额的钱最多只会被拿一次,因为所有超过它的值的都已经在用最小面额的钱填充的时候填充过了),最后就是查询一下,能满足这次分配的方案数,并对储存各种面额的钱的数量的数组进行调整


代码

#include<cstdio>
#include<cstdlib>
using namespace std;

int n,m,x,y,tot,ans;
int a[25],b[25],use[25];

int readln()
{
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while ('0'<=ch&&ch<='9') x=x*10+ch-48,ch=getchar();
    return x;
}

void qsort(int l,int r)
{
    int i=l,j=r,mid=a[rand()%(r-l+1)+l],t;
    do {
        while (a[i]<mid) i++;
        while (a[j]>mid) j--;
        if (i<=j) {
            t=a[i];a[i]=a[j];a[j]=t;
            t=b[i];b[i]=b[j];b[j]=t;
            i++;j--;
        }
    } while (i<=j);
    if (i<r) qsort(i,r);
    if (l<j) qsort(l,j);
}

int min(int x,int y){return x<y?x:y;}

int main()
{
    n=readln();m=readln();
    for (int i=1;i<=n;i++)
    {
        x=readln();y=readln();
        if (a[i]>=m) ans+=y;else a[++tot]=x,b[tot]=y;
    }
    qsort(1,tot);
    while (true)
    {
        int ret=m,t;
        for (int i=tot;i>0;i--)
        {
             t=min(b[i],ret/a[i]);
             ret-=a[i]*t,use[i]=t;
        }
        if (ret>0) for (int i=1;i<=tot&&ret>0;i++) if (b[i]>use[i]) ret-=a[i],use[i]++;
        if (ret<=0)
        {
            t=1e9;
            for (int i=1;i<=tot;i++) if (use[i]) t=min(t,b[i]/use[i]);
            ans+=t;
            for (int i=1;i<=tot;i++) if (use[i]) b[i]-=t*use[i];
        }
        else break;
    }
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值