Luogu P2376
题目想要求出能够支付零花钱的最大星期数,而每种硬币的面额和数量都是固定的,那么就要使浪费尽可能少,所以贪心应该是这道题的正确思路。
贪心策略
- 在输入时判断硬币面额
- 如果面额大于每星期需支付的最小零花钱,那么就说明一枚这种硬币就可以独立支付一个星期的零花钱,就不需要和其他硬币共同支付。这样的话,要使浪费尽可能少,每次就应该只支付一枚这种硬币。所以能支付的星期数 + 这种硬币的数量。
- 如果面额小于等于每星期需支付的最小零花钱,那么就说明一枚这种硬币无法独立支付一个星期的零花钱,因此需要和其他硬币一起支付。我们先将这种硬币的面额和数量存进一个结构体,后续进行判断。
- 对于每一个星期
- 因为当前剩余的所有硬币面额都小于最小零花钱,因此不存在独立支付的情况。
- 当不一定会产生浪费时:因为小面额硬币能够整除所有比它大的面额,所以小面额可以凑出大面额。因此为了避免后续不必要的浪费,我们优先使用大面额硬币。
- 当浪费无可避免时(即本周空缺小于所有面额时):为了使浪费最小,我们优先使用小面额。
- 如果无论如何都无法凑出大于等于最小零花钱的面额,就说明手中剩下的钱无法再支付一个星期的零花钱,结束贪心。
AC代码(含注释)
代码中有防 copy hh,相信认真看的同学们都看出来了。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n, least_num;//面额数,每星期最少零花钱数目
int ans, cnt;
struct money
{
int value;//面额
int num;//数量
}coin[21];
bool cmp(money a, money b){
return a.value > b.value;
}
int main(){
freopen("code.in", "r", stdin);
freopen("code.out", "w", stdout);
cin >> n >> least_num;
for(int i = 1; i <= n; i++){
int a, b;
cin >> a >> b;
if(a >= least_num){//面额大于最小零花钱的硬币,可以独立支付一个星期的零花钱
ans += b;
continue;
}
else {//面额小于最小零花钱的硬币需要和其他硬币一起支付,所以先存起来后续再判断
coin[++cnt].value = a;
coin[cnt].num = b;
}
}
sort(coin + 1, coin + 1 + cnt, cmp);//降序排列
while(1){//无限循环,等到不足以支付了就主动跳出
int k = least_num;
for(int i = 1; i <= cnt; i++){//先用大面额
//当仍需要新的硬币才能支付本周零花钱时
while(k > 0 && coin[i].num && k >= coin[i].value){
k -= coin[i].value;
--coin[i].num;
}
}
for(int i = cnt; i >= 1; i--){//如果一定会产生浪费,那就用尽量小的面额
if(k > 0 && coin[i].num && coin[i].value >= k){
--coin[i].num;
k -= coin[i].value;
}
}
if(k > 0)break;//如果无法凑出大于等于k的值,就说明手中剩下的钱无法再支付一个星期的零花钱
else ans++;//可以凑出k的值,这个星期正常支付
}
cout << ans;
return 0;
}