【HDU5527 2015长春赛区A】【贪心 特判模拟】Too Rich 最多硬币数支付 因子思想 贪心打补丁
2015-11-02 21:37
363 查看
#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 */
相关文章推荐
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法
- C#折半插入排序算法实现方法
- 基于C++实现的各种内部排序算法汇总
- C++线性时间的排序算法分析
- C++实现汉诺塔算法经典实例
- PHP实现克鲁斯卡尔算法实例解析
- C#获取关键字附近文字算法实例