2015 长春赛A && HDU5527 Too Rich【贪心 + DFS】⭐

Too Rich


题意: 给你一定数量的面值分别为 1 , 5 , 10 , 20 , 50 , 100 , 200 , 500 , 1000 , 2000 1,5,10,20,50,100,200,500,1000,2000 1,5,10,20,50,100,200,500,1000,2000的货币,要你用最多数量货币正好凑出 p p p元。

【正向思维的贪心 + DFS】

  • 反向遍历:例如手里有6张十元和一张五十元,要求凑出70元。如果从十元开始贪,是凑不出70元的。所以只能反向遍历,先对面值较大的货币进行贪心,而后对面值较小的进行贪心。
  • 贪多 :不一样的是这题希望用更过的货币数量来凑出零钱,又因为需要反向遍历,所以出现了一些处理上的小困难。定义sum[i] 表示前i种货币一共能够凑出的价值,通过这个我们能够轻松地知道至少需要多少第(i + 1)种货币。
  • 不成倍数关系无法贪心:前一个例子种进行方向遍历还能完成贪心,但是如果是手里有三张20和一张50,要求凑数50元,反向遍历进行贪心又凑不出来了.针对这种“事故”,需要强制选择一张50。
  • 新的问题: 例如手里有六张20元和两张50元,要求凑出140元。通过前面的“教训”,我们会强制选择一张50元,然后去选择20元,发现坏了,20元不能凑出90元,还是不行。其实我们肉眼观察就知道这种情况需要强制选择两张50元。
    然后开始思考,情况复杂时,我到底需要强制选择多少张50元?答案是最多强制选择两张就够了,因为20和50不成倍数关系所以需要强制选择,但是20和两张50则构成了倍数关系,所以再之后就不用管了。至于需要强制选择一张还是两张,则进行两次DFS即可。
#include<bits/stdc++.h>
using namespace std;

int _T, p, ans;
int sum[12], num[12];
int val[] = {0, 1, 5, 10, 20, 50, 100, 200, 500, 1000, 2000};

void dfs(int res, int idx, int cnt) {	//剩余价值res,第idx种货币,货币数量cnt
	if(res < 0) return;
	if(idx == 0) {
		if(res == 0)	ans = max(ans, cnt);
		return;
	}
	int cur = max(res - sum[idx - 1], 0);
	int cur_cnt = cur / val[idx] + (cur % val[idx] ? 1 : 0); //如果除不尽,需要强制多选一张货币
	if(cur_cnt <= num[idx])	//保证该种货币数量充足才能进行dfs
		dfs(res - cur_cnt * val[idx], idx - 1, cnt + cur_cnt);
	cur_cnt++;	//可能需要强制多选两张货币
	if(cur_cnt <= num[idx])
		dfs(res - cur_cnt * val[idx], idx - 1, cnt + cur_cnt);
}

void run() {
	ans = -1;
	scanf("%d", &p);
	for(int i = 1; i <= 10; i++) {
		scanf("%d", num + i);
		sum[i] = sum[i - 1] + val[i] * num[i];	//前i种货币一共能够凑出的价值
	}
	dfs(p, 10, 0);
	printf("%d\n", ans);
}

int main() {
	scanf("%d", &_T);
	while(_T--)	run();
    return 0;
}

【逆向思维的贪心 + DFS】

  • 求sum - p:用尽可能多的货币去凑出p,反过来当我们知道所有货币一共能够凑出的总价值sum,题目就可以变成求用最少的货币数量去凑出sum - p。
  • 强制选择 :强制选择方面,则是变成了强制少选一张,细节处理上类似。
#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;

int _T, p, ans;
int num[12], val[] = {0, 1, 5, 10, 20, 50, 100, 200, 500, 1000, 2000};

void dfs(int tot, int idx, int cnt) {
	if(tot < 0)	return;
	if(idx == 0) {
		if(tot == 0) ans = min(cnt, ans);
		return;
	}
	int tmp = min(num[idx], tot / val[idx]);
	dfs(tot - tmp * val[idx], idx - 1, cnt + tmp);
	if(tmp) dfs(tot - (tmp - 1) * val[idx], idx - 1, cnt + tmp - 1);	//强制少选一张
}

void run() {
	ans = inf;
	int tot = 0, cnt = 0;
	scanf("%d", &p);
	for(int i = 1; i <= 10; i++) {
		scanf("%d", num + i);
		cnt += num[i];
		tot += val[i] * num[i]; 
	}
	dfs(tot - p, 10, 0);
	printf("%d\n", ans == inf ? -1 : cnt - ans);
}

int main() {
	scanf("%d", &_T);
	while(_T--)	run();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值