【HDU5527 2015长春赛区A】【贪心 特判模拟】Too Rich 最多硬币数支付 因子思想 贪心打补丁

84 篇文章 5 订阅
48 篇文章 0 订阅
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<string>
#include<algorithm>
#include<time.h>
#include<bitset>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T> inline void gmax(T &a,T b){if(b>a)a=b;}
template <class T> inline void gmin(T &a,T b){if(b<a)a=b;}
const int N=55,M=0,Z=1e9+7,maxint=2147483647,ms31=522133279,ms63=1061109567,ms127=2139062143;const double eps=1e-8,PI=acos(-1.0);//.0
int casenum,casei;
UI m;
int a[12],c[12],b[12],e[12];
int ans;
void init()
{
    c[1]=1;
    c[2]=5;
    c[3]=10;
    c[4]=20;
    c[5]=50;
    c[6]=100;
    c[7]=200;
    c[8]=500;
    c[9]=1000;
    c[10]=2000;
}
int cntmin(UI tmp,int p)
{
    int num=0;
    for(int i=p;i>=1;i--)
    {
        if(e[i]==-1)
        {
            int g=min((UI)b[i],tmp/c[i]);
            num+=g;
            tmp-=g*c[i];
        }
        else
			
        {
            int g=min((UI)b[i],tmp/c[i]);
            if(g&1)--g;
            num+=g;
            tmp-=g*c[i];
        }
    }
    if(tmp==0)return num;
    else return -1;
}
void tryit()
{
    if(e[5]>a[5]||e[8]>a[8])return;
    MS(b,0);
    int num=e[5]+e[8];
    UI tmp=e[5]*50+e[8]*500;
    for(int i=1;i<=10;i++)
    {
        if(e[i]==-1)
        {
            num+=a[i];
            b[i]=a[i];
            tmp+=a[i]*c[i];
        }
        else
        {
            b[i]=a[i]-e[i];
            if(b[i]&1)--b[i];
            num+=b[i];
            tmp+=b[i]*c[i];
        }
        if(tmp>=m)
        {
            int more=tmp-m;
            int over=cntmin(more,i);
            if(~over)
            {
                num-=over;
                gmax(ans,num);
            }
            return;
        }
    }
}
int main()
{
    init();
    scanf("%d",&casenum);
    for(casei=1;casei<=casenum;casei++)
    {
        scanf("%d",&m);
        for(int i=1;i<=10;i++)scanf("%d",&a[i]);
        MS(e,-1);ans=-1;
        e[5]=0;e[8]=0;tryit();
        e[5]=0;e[8]=1;tryit();
        e[5]=1;e[8]=0;tryit();
        e[5]=1;e[8]=1;tryit();
        printf("%d\n",ans);
    }
    return 0;
}
/*
【trick&&吐槽】
这题的代码量也并不大,也不涉及高级的数据结构和算法,然而AC的人却很少,比赛时更是只有几个。
一定是大家遇到一个难以处理的问题就放弃这道题了。
其实,从简单入手展开思维,发现问题后深入思考,也许是可以通过特殊判定打补丁法AC。

【题意】
T(20000)组数据。

我们有10种不同价值的硬币
面值c[]分别是{1,5,10,20,50,100,200,500,1000,2000}
数量a[]可能是[0,1e5]。
让你输出最多使用多少枚硬币,可以恰好使其货币价值总和为m([0,1e9])。
如果没有这样的方案,输出-1

【类型】
模拟,讨论

【分析】
如果采用最傻瓜式的贪心,我们一定是使得每枚货币的面值尽可能低。
即,如果有面值更低的硬币,我们优先使用它;它用完之后,才考虑面值更大的硬币。
这种做法显然有问题。因为它不能保证我们恰好凑得硬币的总价值为m。
1,可能不够——直接-1
2,可能会溢出,这个要怎么办呢?

我们发现,我们可以算得溢出的硬币面值more。
如果我们能求出,之前选取硬币中,凑成面值总额为more的最少硬币个数,这样减一下即可得到答案?

但是这里就不能再继续傻瓜下去啦,我们可能会面临一个问题——
尽管我们之前的做法,留给了我们回退的余地,但是其依然有可能出现无法回退,错误判定为无解的状况。
比如:我们有50元硬币*1,20元硬币*3,我们想要凑得面额为50的硬币。
这种做法会先算入20元硬币*3,然后不论怎么回退都没有办法达成目的。

为什么会出现这种情况呢?
我们发现,这是因为,对于给定的所有面值,
每个面值都是比它大的所有面值的约数,除了(20<->50),(200<->500)这两个关系。

如果不存在这两个特殊关系,那么我们可以就采取一开始的贪心原则。
因为小的面值是比它大的所有面值的因子,所以大硬币所能拼凑的面额它一定能拼成。
也就是说,它不仅在数量上保证了更多,也在拼凑额度的功能上更优。
所以这时可以贪心:永远都拿面额更小的,直到面额>=m。
这里还是会出现面额可能超出的情况,但是首先超出的是大面额。而我们所有的小面额都取了。
而这时,我们操作的灵活性会是最大的,接下来的调整一定可以完成。

如果采取这个贪心做下去,那就只需要解决这两个特殊关系啦。
如何解决呢?
我们发现问题就是:
50可能取奇数次,这是20所凑不出的。
500可能取奇数次,这是200所凑不出的。
于是我们枚举以下四种情况
(50和500都是偶数个,不先取50和500)
(50为奇数个,500为偶数个,即我们先取一个50)
(50为偶数个,500为奇数个,即我们先取一个500)
(50为奇数个,500为奇数个,即我们先取一个50和一个500)
之后对于50和500都成对地取。
每次取50或500的时候就取偶数个。然而消除的时候也消除偶数个。
这样的枚举,就消除了这个两个特殊关系的影响啦。

为什么呢,我们可以分析下:
先以50为例,我们先枚举最后取50的奇偶性,再贪心从小到大取数,然后溢出了,开始考虑移除(移除最少的个数)——
不论最后取的50是奇数个还是偶数个,对于每个成对的50(即100)而言,
结合之间的"约数结论",只要我们剩余的数的总和大于等于100,那么它们是一定能够拼凑出100的。
于是我们有:这里移除2个50肯定是移除最少的个数,是最优的。

同理,对于500而言,先决定它最后的奇偶性。
然后对于每个成对的500,之前的所有数都是1000的因子。如果数值之和达到1000,便一定能凑得1000。
于是只要保证"之下的所有数的数值之和不减小到不够",这里就可以直接移除这个1000,肯定更优。

这题就这么做完啦。用贪心+模拟AC这道金牌题(甚至可以说是WF题)了哦^_^~

【时间复杂度&&优化】
O(T10)

【数据】
input(1个1,3个20,1个50,1个200——表明傻叉贪心是错误的)
1
250 1 0 0 3 1 0 1 0 0 0
output
2

input(5个20,100个50——表明)
1
190 0 0 0 5 100 0 0 0 0 0
output
5

*/

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值