洛谷2376 奶牛工资(贪心)

题目描述
贝西工作勤勤恳恳,她每月向约翰索要C 元钱作为工资。约翰手上有不少钱,他一共有N 种面额的钞票。第i 种钞票的面额记作Vi,约翰有Ki 张。钞票的面额设定是比较合理的,保证所有大面额的钞票都是所有小面额钞票的整数倍。假设约翰每个月给贝西发一次工资,那么这些钱够发几个月的工资呢?贝西不会找零,如果约翰发的钱大于C 元,多余的部分就算是贝西的奖励了。
输入输出格式
输入格式:
第一行:两个整数N 和C,1 ≤ N ≤ 20, 1 ≤ C ≤ 109
第二行到第N + 1 行:第i + 1 行有两个整数Vi 和Ki,1 ≤ Vi ≤ 109; 1 ≤ Ki ≤ 106
输出格式:
单个整数:表示约翰最多能给贝西发几个月的工资
输入输出样例
输入样例#1:
3 6
10 1
1 100
5 120
输出样例#1:
111
说明
第一个月先给一张十元的,接下来十个月每个月都给两张五元的,最后一百个月每月给一张一元的和一张五元的。

正解思路:根据贪心策略,先把面值大于C的钞票花出去,不会使答案更差。对于面值小于C的钞票,先用较大面值的钞票凑,找到恰好能得到花费为c的情况(这样显然会让花费尽量少,显然更优)。若最后凑不出恰好为C的情况,尽量选择小面值的钞票去凑,一直到结束为止。

这题被拿来当考试题了,wa的一塌糊涂。错解思路:dfs+剪枝。也是先排一下序(从大到小),然后去搜恰好等于c的情况。然后剩下的再去大的和小的凑,如果能凑出来就凑,否则就加起来继续找继续凑,直到最后>c,ans++,一个类似于左右指针往中间移的思想。由于洛谷数据太水的缘故,这个可以在洛谷AC5个点,T4个点。一并贴上代码。

错解:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=25;
struct dqs
{
    ll mz,num;//mz==面值 
}hh[maxn],ha[maxn];
ll used[maxn];
bool cmp(dqs a,dqs b)
{
    return a.mz>b.mz;
}
ll ans=0,n,c,tot;
void dfs(ll x,ll shu,ll pre,ll minnum)
{
    if(shu>c) return;
    if(used[pre]>ha[pre].num) return;
    if(shu==c)
    {
        ans+=minnum;
        for(ll i=1;i<=tot;i++)
            ha[i].num-=minnum*used[i];
    }
    for(ll i=1;i<=tot;i++)
    {
        used[i]++;
        dfs(i,shu+ha[i].mz,x,min(minnum,ha[i].num/used[i]));
        used[i]--;
    }
}
int main()
{
    ll shu=0;
    bool flag=0;
    scanf("%lld%lld",&n,&c);
    for(ll i=1;i<=n;i++)
    {
        scanf("%lld%lld",&hh[i].mz,&hh[i].num);
        if(flag==0)
        shu+=hh[i].mz*hh[i].num;
        if(shu>=c) flag=1;
    }
    if(!flag)
    {
        puts("0");
        return 0;
    }
    sort(hh+1,hh+n+1,cmp);
    for(ll i=1;i<=n;i++)
    {
        if(hh[i].mz>=c) ans+=hh[i].num;
        else ha[++tot]=(dqs){hh[i].mz,hh[i].num};
    }
    dfs(1,0,0,1e9);
    for(ll i=1;i<=tot;i++)
    {
        ll cj=ha[i].num*ha[i].mz;
        if(cj>c)
        {
            ll sss=ha[i].mz,sc;
            while(sss<c)
                sss+=ha[i].mz;
            sc=cj/sss;
            ans+=sc;
            ha[i].num-=sc;
        }
    }
    ll preshu=0,prei=0,lasti=0,lastshu=0;   
    for(ll i=1;i<=tot;i++)
    {
        if(ha[i].num!=0)
        {
            preshu=ha[i].mz;
            prei=i;
            break;
        }
    }
    for(ll i=tot;i>=1;i--)
    {
        if(ha[i].num!=0)
        {
            lastshu=ha[i].mz;
            lasti=i;
            break;
        }
    }
    while(prei!=lasti) 
    {
        if(preshu+lastshu*ha[lasti].num>=c)
        {
            ans++;
            lasti--;
            if(ha[prei].num--==0)
                prei++;
        }
        else
        {
            preshu+=lastshu*ha[lasti].num;
            lasti--;
        }
    }
    printf("%lld\n",ans);
    return 0;
} 

正解:

/*洛谷P2619*/ 
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=200005; 
struct dqs
{
    int mz,num;
}hh[maxn];
bool cmp(dqs a,dqs b)
{
    return a.mz>b.mz;
}
int main()
{
    int n,c,ans=0,tot=0;
    scanf("%d%d",&n,&c);
    for(int i=1;i<=n;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if(a>=c)    ans+=b;
        else    hh[++tot]=(dqs){a,b};
    }
    sort(hh+1,hh+tot+1,cmp);
    while(true)
    {
        int tmp=c;
        for(int i=1;i<=tot;i++)
        {
            while(tmp>=hh[i].mz&&hh[i].num)
            {
                int shu=min(tmp/hh[i].mz,hh[i].num);
                tmp-=shu*hh[i].mz;
                hh[i].num-=shu;
            }
        }
        if(tmp>0)
        {
            for(int i=tot;i>=1;i--)
                if(tmp<=hh[i].mz&&hh[i].num)
                {
                    hh[i].num--;
                    tmp=0;
                    break;
                }
        }
        if(tmp>0) break;
        ans++;
    }
    printf("%d\n",ans);
    return 0;
}
/*
4 14
5 1
6 1
7 1
11 1*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值