题意:给定10种面额的货币和它们的数量上限,问构造出恰好总额为P的最小张数,无解输出-1
T=2e4,p<=1e9,c[i]<=1e5
思路:From https://blog.csdn.net/snowy_smile/article/details/49592521
如果采用最傻瓜式的贪心,我们一定是使得每枚货币的面值尽可能低。
即,如果有面值更低的硬币,我们优先使用它;它用完之后,才考虑面值更大的硬币。
这种做法显然有问题。因为它不能保证我们恰好凑得硬币的总价值为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,肯定更优。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<ctime> 6 #include<cmath> 7 #include<algorithm> 8 #include<iomanip> 9 #include<vector> 10 #include<map> 11 #include<set> 12 #include<bitset> 13 #include<queue> 14 #include<stack> 15 using namespace std; 16 typedef long long ll; 17 typedef unsigned int uint; 18 typedef unsigned long long ull; 19 typedef pair<int,int> PII; 20 typedef vector<int> VI; 21 #define fi first 22 #define se second 23 #define MP make_pair 24 #define N 20 25 #define M 110000 26 #define eps 1e-8 27 #define pi acos(-1) 28 #define oo 1000000000 29 #define MOD 10007 30 31 const ll c[11]={0,1,5,10,20,50,100,200,500,1000,2000}; 32 33 int a[N],b[N],d[N],n,ans; 34 35 ll min(ll x,ll y) 36 { 37 if(x<y) return x; 38 return y; 39 } 40 41 int isok(ll s,int p) 42 { 43 int num=0; 44 for(int i=p;i>=1;i--) 45 { 46 if(d[i]==-1) 47 { 48 int t=min(b[i],s/c[i]); 49 num+=t; 50 s-=t*c[i]; 51 } 52 else 53 { 54 int t=min(b[i],s/c[i]); 55 if(t&1) t--; 56 num+=t; 57 s-=t*c[i]; 58 } 59 } 60 if(s==0) return num; 61 return -1; 62 } 63 64 void solve() 65 { 66 if(d[5]>a[5]||d[8]>a[8]) return; 67 memset(b,0,sizeof(b)); 68 int s1=d[5]+d[8]; 69 ll s2=d[5]*50+d[8]*500; 70 for(int i=1;i<=10;i++) 71 { 72 if(d[i]==-1) 73 { 74 s1+=a[i]; 75 b[i]=a[i]; 76 s2+=c[i]*a[i]; 77 } 78 else 79 { 80 b[i]=a[i]-d[i]; 81 if(b[i]&1) b[i]--; 82 s1+=b[i]; 83 s2+=c[i]*b[i]; 84 } 85 if(s2>=n) 86 { 87 int t=isok(s2-n,i); 88 if(t!=-1) 89 { 90 s1-=t; 91 ans=max(ans,s1); 92 } 93 return; 94 } 95 } 96 } 97 98 99 int main() 100 { 101 //freopen("hdoj5527.in","r",stdin); 102 //freopen("hdoj5527.out","w",stdout); 103 int cas; 104 scanf("%d",&cas); 105 for(int v=1;v<=cas;v++) 106 { 107 scanf("%d",&n); 108 for(int i=1;i<=10;i++) scanf("%d",&a[i]); 109 memset(d,-1,sizeof(d)); 110 ans=-1; 111 d[5]=0; d[8]=0; solve(); 112 d[5]=0; d[8]=1; solve(); 113 d[5]=1; d[8]=0; solve(); 114 d[5]=1; d[8]=1; solve(); 115 printf("%d ",ans); 116 } 117 return 0; 118 } 119 120