2023-12-23 poj3040 Allowance 数学证明 贪心算法 代码实现

一、算法伪码

allowance_greedy_distribution(coins, c):
// coins记录了硬币数量和面值,c是每天至少发放的津贴
	将coins按照coins.value从大到小排序
	day=0
	while true:
	    today=0
	    for coin in coins:
		    尽可能选择面值大的硬币但不使其溢出
		if 还有硬币:
			使用还有的最小一种硬币,并使其数量-1
		if 没有硬币:
			break

二、引理

在硬币数目不受限制的情况下,

引理 P n P_n Pn:一组货币{ B 1 B_1 B1, B 2 B_2 B2,…, B n B_{n} Bn}满足整除性质,具有价值 W n = ∑ i = 1 n k i B i W_n=\sum_{i=1}^{n}{k_iB_i} Wn=i=1nkiBi,可以硬币数不增的将{ B 1 B_1 B1, B 2 B_2 B2,…, B n − 1 B_{n-1} Bn1}尽数替换为 B n B_n Bn并且这个过程结束后,满足 W n − 1 ′ < B n W_{n-1}^{'}<B_n Wn1<Bn详细证明请见2023-12-23 硬币选择问题 硬币找零问题 poj3040 贪心选择性质 数学证明-CSDN博客的引理部分。

三、贪心替换引理

硬币数量受限的贪心性质:至少存在一个从大到小选择硬币且满足硬币剩余数量都大于0的替换

引理:在硬币数量受限制时,存在一个满足贪心性质的替换。

证明:采用构造解法证明。
根据题意,至少有一组不一定满足贪心性质的解{ λ 1 , λ 2 , . . . , λ n \lambda _1,\lambda _2,...,\lambda _n λ1,λ2,...,λn}使得 W n = ∑ i = 1 n λ i B i = W W_n=\sum_{i=1}^n\lambda_iB_i=W Wn=i=1nλiBi=W
先假设硬币数量不受限制,由 P n P_n Pn,一定有一组满足贪心性质的替换{ λ 1 ′ , λ 2 ′ , . . . , λ n ′ \lambda _{1}^{'},\lambda _{2}^{'},...,\lambda _{n}^{'} λ1,λ2,...,λn},且满足 λ i < = λ i ′ , i > = 2 \lambda_i<=\lambda_{i}^{'},i>=2 λi<=λii>=2以及 λ i < = c o i n s [ i ] . n u m b e r s , i > = 1 \lambda_i<=coins[i].numbers,i>=1 λi<=coins[i].numbersi>=1
再按照以下方法构造一个满足贪心性质的替换:
λ n ′ 到 λ 2 ′ \lambda _{n}^{'} 到\lambda _{2}^{'} λnλ2依次进行如下操作:
{ 将超出数额的硬币换成若干个面值次大的硬币 λ k > c o i n [ k ] . n u m b e r s c o n t i n u e λ k < = c o i n [ k ] . n u m b e r s \left\{\begin{matrix} 将超出数额的硬币换成若干个面值次大的硬币 & \lambda_k >coin[k].numbers \\ continue & \lambda_k<=coin[k].numbers \\ \end{matrix}\right. {将超出数额的硬币换成若干个面值次大的硬币continueλk>coin[k].numbersλk<=coin[k].numbers
利用这个方法得到一组除了 λ 1 ′ ′ \lambda _{1}^{''} λ1′′外一定满足贪心性质且合法的替换{ λ 1 ′ ′ , λ 2 ′ ′ , . . . , λ n ′ ′ \lambda _{1}^{''},\lambda _{2}^{''},...,\lambda _{n}^{''} λ1′′,λ2′′,...,λn′′}。

因为 λ k ′ ′ \lambda _{k}^{''} λk′′(k>=2)不是coins[k].numbers就是 λ k ′ \lambda _{k}^{'} λk,所以一定有 λ i < = λ i ′ ′ , i > = 2 \lambda_i<=\lambda_{i}^{''},i>=2 λi<=λi′′i>=2
下面利用反证法证明其是一组合法替换。
假设这组替换不合法,则有这组替换 W n ′ ′ = ∑ i = 2 n λ i ′ ′ B i + λ 1 ′ ′ B 1 > = ∑ i = 2 n λ i B i + λ 1 ′ ′ B 1 > ∑ i = 1 n λ i B i = W W_{n}^{''}=\sum_{i=2}^{n}\lambda_{i}^{''}B_i+\lambda_{1}^{''}B_1>=\sum_{i=2}^n\lambda_iB_i+\lambda_{1}^{''}B_1>\sum_{i=1}^n\lambda_iB_i=W Wn′′=i=2nλi′′Bi+λ1′′B1>=i=2nλiBi+λ1′′B1>i=1nλiBi=W,这与构造过程的W不变相矛盾,故这个替换一定是一组合法替换。

四、贪心选择性质

该Allowence_distribution问题的贪心选择性质为:至少包含一个每次尽可能少给牛但不低于c,且每次选尽可能大但是工资不溢出的构造得到的最优解,

假设 D i D_i Di为第i天的一个分配,其价值为 D i . v a l u e D_i.value Di.value,其分配为 D i . λ [ ] D_i.\lambda[] Di.λ[]

第一步:

假设有一个解是最优解,但是不满足上述贪心选择性质,由于 D [ ] D[] D[]与其天数顺序无关,可以进行排序得到它的一个同构解,并且按照这个顺序进行构造时,每一步的多余工资 D i . c o s t = D i . v a l u e − c > = D 贪心 i . c o s t D_i.cost=D_i.value-c>=D_{贪心i}.cost Di.cost=Di.valuec>=D贪心i.cost
倘若解 D 贪心 [ ] D_{贪心}[] D贪心[]能够发n天工资,则有总价值 W = n c + ∑ i = 1 n D 贪心 i . c o s t + D 贪心 . l a s t = n c + ∑ i = 1 n D i . c o s t + D . l a s t W=nc+\sum_{i=1}^{n}D_{贪心i}.cost+D_{贪心}.last=nc+\sum_{i=1}^{n}D_{i}.cost+D.last W=nc+i=1nD贪心i.cost+D贪心.last=nc+i=1nDi.cost+D.last
则对于每个n都有:
D 贪心 . l a s t > = D . l a s t D_{贪心}.last>=D.last D贪心.last>=D.last
故只要 D . l a s t D.last D.last能够一天工资, D 贪心 . l a s t D_{贪心}.last D贪心.last就至少够发一天工资。

第二步:

则假设有一个最优解 D i D_i Di,由贪心替换引理,可以得到一个最优解的同构 D i ′ D_{i}^{'} Di
证毕。

五、代码实现

可以直接运行的示例:

#include <iostream>
#include <vector>
#include <queue>
#include <list>
#include <math.h>
//#include <assert.h>
using namespace std;
//using Datatype = int;	
typedef long long Datatype;
typedef long long Elemtype;

struct COIN {
	Datatype value;
	Elemtype numbers;
};

Elemtype allowence_distribution_greedy(list<COIN>& coins, Datatype c); // 接受硬币的数据和每天的津贴数,返回够发的天数

int main() {
	list<COIN> coins = { {10,1},{1,100},{5,120} };
	cout << allowence_distribution_greedy(coins, 6);
}


Elemtype allowence_distribution_greedy(list<COIN>& coins, Datatype c) {
	coins.sort([](COIN a, COIN b) { return a.value > b.value; });
	Elemtype days = 0;
	while (1) {
		Datatype today = 0;
		auto it = coins.begin();
		while (it != coins.end())
		{
			if (c == today)break;
			if (c < today + it->value)it++;
			else {
				today += it->value;
				it->numbers--;
				if (0 == it->numbers)coins.erase(it++);
			}
		}
		if (c == today)days++;
		else if (!coins.empty()) {
			// 只要还有硬币,必然再加一块硬币就超过c
			days++;
			auto it = --coins.end();
			it->numbers--;
			//assert(today + it->value > c);
			if (0 == it->numbers)coins.erase(it);
		}
		if (coins.empty())break;
	}
	return days;
}

六、补充
补充部分和本文无任何干系,仅供看个乐子。

  1. 本题使用了两次贪心选择性质。
  2. 贪心选择性一般而言,都出现在顺序无关的情况下。
  • 33
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值