目录
一,关于进制的网络热梗
(1)世界上有10种人,一种懂二进制,另一种不懂。
PS:如果你有疑问,那你就是那个不懂二进制的
(2)这个世界上有三种人:会数数的和不会数数的。
PS:因为我就是那个不会数数的
(3)为什么程序员总是分不清万圣节和圣诞节?
PS:因为 Oct 31 == Dec 25
二,关于十进制的起源
PS:左边的是外星人,他的手指数量和地球人不同。
这个关于外星人和地球人的插画,是我的进制梗最爱,不仅幽默,而且有深度。
这里揭示了2个重要事实,第一,人类使用十进制是因为有十个手指头,第二,every base is base 10
三,货币组合问题
1,有限集合的最小不可表示的和
(1)问题
给定一个由正整数构成的有限集合,求最小的正整数x,使得x不是该集合的任意子集的所有数之和。
例如,对于集合{1,2,5,6},x=4是最小的不可表示为子集和的正整数。
(2)思路
首先我们把集合转化成一个递增的数列,核心思路是从前往后的归纳法,但是如何提炼命题呢?
对于任意集合A,可以表示的和的范围最多是从1到S(A)的闭区间,S(A)是A中所有元素的和。
在这个前提下,多试几个例子,观察规律,我们就能得到如下的核心猜想。
(3)命题P(n)
n个数组成的递增数列,最小不可表示的和一定等于左边若干个数的和+1。
(4)第二数学归纳法
如果当n=1,2,3...k时,P(n)都成立,那么P(k+1)成立吗?
好像很难直接推出来。
(5)加强数学归纳法
我们把命题P变成加强命题Q:
n个数组成的递增数列,一定可以分成左右2个部分(2个都可能为空),最小不可表示的和等于左边所有数的和+1,且右边所有数都大于这个数。
对于Q,利用第二数学归纳法很容易证明。
于是我们就用加强数学归纳法证明了P。
(6)如何有效求出最小不可表示的和
其实还是需要用Q,因为P太弱了。
只需要找到第一个满足,一个数比前面所有数的和+1更大,那就找到了答案。
2,有限数列的最小不可表示的和
假如给定的数中有重复的,思路和结论不变。
力扣 2952. 需要添加的硬币的最小数量
给你一个下标从 0 开始的整数数组 coins
,表示可用的硬币的面值,以及一个整数 target
。
如果存在某个 coins
的子序列总和为 x
,那么整数 x
就是一个 可取得的金额 。
返回需要添加到数组中的 任意面值 硬币的 最小数量 ,使范围 [1, target]
内的每个整数都属于 可取得的金额 。
数组的 子序列 是通过删除原始数组的一些(可能不删除)元素而形成的新的 非空 数组,删除过程不会改变剩余元素的相对位置。
示例 1:
输入:coins = [1,4,10], target = 19 输出:2 解释:需要添加面值为 2 和 8 的硬币各一枚,得到硬币数组 [1,2,4,8,10] 。 可以证明从 1 到 19 的所有整数都可由数组中的硬币组合得到,且需要添加到数组中的硬币数目最小为 2 。
示例 2:
输入:coins = [1,4,10,5,7,19], target = 19 输出:1 解释:只需要添加一枚面值为 2 的硬币,得到硬币数组 [1,2,4,5,7,10,19] 。 可以证明从 1 到 19 的所有整数都可由数组中的硬币组合得到,且需要添加到数组中的硬币数目最小为 1 。
示例 3:
输入:coins = [1,1,1], target = 20 输出:3 解释: 需要添加面值为 4 、8 和 16 的硬币各一枚,得到硬币数组 [1,1,1,4,8,16] 。 可以证明从 1 到 20 的所有整数都可由数组中的硬币组合得到,且需要添加到数组中的硬币数目最小为 3 。
提示:
1 <= target <= 105
1 <= coins.length <= 105
1 <= coins[i] <= target
class Solution {
public:
int minimumAddedCoins(vector<int>& coins, int target) {
sort(coins.begin(), coins.end());
coins.push_back(target + 1);
int s = 0, ans = 0;
for (auto x : coins) {
while (x > s + 1) {
ans++;
s += s + 1;
}
s += x;
if (s >= target)return ans;
}
return -1;
}
};
3,分金条问题
假设你是金主爸爸,你要雇一个人来给你打工一周,并且每天付他的报酬必须是一样的。
你手里有1根金条,还有1把无比锋利的可以切开金条的刀,但是你只能切2刀。
你要怎么切,怎么分配,才能保证每天付给工人的报酬是一样的呢?
答案:
按照1/7 2/7 4/7分成3段。
四,货币贪心问题
1,无限货币的贪心问题
问题:当前有面值分别为2角5分,1角,5分,1分的硬币,请给出找n分钱的最佳方案(要求找出的硬币数目最少)。
解析:
有25分,10分,5分,1分的,那么1分的肯定有n%5个,问题归结为5分,2分,1分的问题。
2个1分不如1个2分,所以1分最多1个, 3个2分不如1个5分和1个1分,所以2分最多2个。
只有6种情况,除掉1个1分2个2分的情况有5种情况,按照n%5直接对应就是唯一的最优解。
#include<iostream>
using namespace std;
int main()
{
int n ;
cout << "请输入多少分\n";
cin >> n;
if (n > 0) //25,10,5,1
{
cout << "1分的要" << n % 5 << "个";
n = n / 5; //5,2,1
int m = n % 5;
int a1=0, a2=0;
if (m == 1)a1 = 1;
if (m == 2)a2 = 0;
if (m == 3)a1 = a2 = 1;
if (m == 4)a2 = 2;
cout << "\n5分的要" << a1 << "个\n1角的要"<<a2<<"个\n2角5分的要"<<n/5<<"个";
}
system("pause>nul");
return 0;
}
2,有限货币的贪心问题
CSU 1926 使用最少的硬币
题目:
Description
在小X生活的国家里,很多面额都有硬币。小X的存钱罐里有四种硬币,分别是5元的、10元的、20元的和50元。当他要付钱时,总是希望使用的硬币数量最少。现在告诉你小X拥有的四种硬币的数量,请你帮小X求出最好的付款方案(可能方案不存在)。
Input
输入包含不超过10组数据。 每组数据由一行五个用空格隔开的整数A、B、C、D、S组成(0<=A,B,C,D<=1000000,1<=S<=10000000),分别表示小X拥有的5元硬币、10元硬币、20元硬币和50元硬币的个数,以及小X现在需要付的钱数(单位:元)。
Output
对于每组数据,如果小X无法付款,则输出一行一个整数-1;否则输出一行五个用空格隔开的整数a、b、c、d、s,分别表示付款方案中小X需要用掉的5元硬币、10元硬币、20元硬币和50元硬币的数量,以及小X需要花费的总硬币数。
Sample Input
1 2 3 4 35
1 2 3 4 567
Sample Output
1 1 1 0 3 -1
代码:
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int A, B, C, D, S;
while (cin >> A >> B >> C >> D >> S)
{
if (S % 5 || S % 2 && A == 0)
{
cout << -1 << endl;
continue;
}
S /= 5; //1,2,4,10
int a, b, c, d, s;
if (A > S % 2 + 1 || B > 0)//贪心
{
d = min(S / 10, D), S -= d * 10;
c = min(S / 4, C), S -= c * 4;
b = min(S / 2, B), S -= b * 2;
a = min(S, A), S -= a;
s = a + b + c + d;
if (S)cout << -1 << endl;
else cout << a << " " << b << " " << c << " " << d << " " << s << endl;
continue;
}
a = S % 2, S /= 2;//2,5
if (S % 2 && D == 0)
{
cout << -1 << endl;
continue;
}
d = min((S-S%2*5) / 10, (D - S % 2) / 2) * 2 + S % 2, S -= d * 5;
c = min(S / 2, C), S -= c * 2;
b = 0;
s = a + b + c + d;
if (S)cout << -1 << endl;
else cout << a << " " << b << " " << c << " " << d << " " << s << endl;
}
return 0;
}
五,货币面额设计
我们不讨论防伪。工艺。文化艺术等等,只讨论面额(纯数字)。
面额设计里面的学问,比一般人想象的多的多。
1,面额的上下限
面额的上下限,取决于购买力,太小了没意义,太大了也不是很方便。
2,交易额颗粒度
假设所有的交易都是某个数额x的整数倍,那么x是多少?
对于我们来说,x就是一分钱。
实际情况可能更复杂,暂且这么理解吧。比如,我们的交易可以是,我每天给你0.3分,那我实际上可以每天给你打一个0.3分的欠条,凑足一定数量随时可以来找我折算。这些可以理解为虚拟交易,真实交易还是一分钱的整数倍。
那么,必须所有货币都是x的整数倍吗?为什么?
3,货币组合问题
如果要让任何数额的交易,都可以直接付钱,不需要找钱,怎么设计面额?
这个问题有一个暴力解,只要有x就行,因为x的数量是无限的,而所有交易都是x的倍数,所以可以只用x完成付钱,不需要找钱。
然而,有一个现实问题,只用x付钱,数量可能太多,所以一定需要组合付款。
很容易首先想到,要想满足多种付款金额,且用的货币数量较少(无论是否允许找钱),一定是所有货币都是x的整数倍是最优的。
进一步地,考虑直接付钱不找钱,使用货币数量尽可能少,货币的整体分布应该是接近等比数列。
4,货币贪心问题
为什么我们的货币面额是1,2,5,10,20,50,100?换成1,4,5,10,40,50,100行不行?
这是肯定不行的。1,2,5,10,20,50,100用贪心可以得到最优解,而1,4,5,10,40,50,100则不行。
5,货币进制问题
货币面额按照十进制去设计,更方便计算。