E - Payment(思维)
题意:
在一个国家纸币只有 +1 种面额,分别1,10,
,
,…,
。你需要去支付n元,可能是你付出了一个更大的钱数,对方再找回你差值的钱。
问整个交易过程最少需要用到多少个纸币?
数据范围:
1 ≤ N ≤
思路:
对于给出数的每一位,用nas直接记录每一位所需的钞票数,用res记录进位所需的钞票数,若:1.x<5,直接自己给x即可。
2.x>5,别人给10-x。
3.x=5,前一位满足大于等于5时进位。
不是简单的四舍五入,如:15,如果进位算15->20 -> 5+2=7,实际为1+5=6;65,如果进位算65->70->100 -> 5+3+1=9;直接算是6+5=11。
Code:
#include<iostream>
#include<algorithm>
using namespace std;
//#define int long long
void solve()
{
string s;
cin >> s;
int ans = 0, res = 0;
s = '0' + s; //在s前加一位'0',防止首位进位
int len = s.size();
//计算进位需要的钞票数
for (int i = len - 1; i >= 1; i--)
{
if (s[i] > '5')
{
res += 10 - (s[i] - '0');
s[i] = '0';
s[i - 1]++;
}
else if (s[i] == '5' && s[i - 1] >= '5')
{
res += 10 - (s[i] - '0');
s[i] = '0';
s[i - 1]++;
}
}
//算上每一位需要的钞票数
for (int i = 0; i < len; i++)
ans += s[i] - '0';
cout << ans + res << endl;
}
signed main()
{
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
DP思路:
对于要支付的钱,用字符串存,从低位到高位,每一位只有两种选择:
1.支付 x 张
2,支付 1 张 ,找回 10-x 张
的货币。
状态表示:f[0][i] 表示第 i 位用第一种方法的最小票数,f[1][i] 表示第i位用第二种方法的最小票数。(最小票数指的是第 i~len 位所用的票数之和最小)
集合划分:根据第 i+1 位的状态为依据将第 i 位的两种状态各自划分为两类:
f[0][i]:(1)当第 i+1 位的选择是1时,即 f[0][i+1],第 i+1 位产生的票数是支付时的 x 张,对第 i 位无影响,此时的 f[0][i] 就等于 f[0][i+1] 加上第 i 位的票数x:即 f[0][i+1] + (s[i] - '0');
(2)当第 i+1 位的选择是2时,即 f[1][i+1],第 i+1 产生的票数时店员找回的 10-x 张,同时还需要第 i 位的1张,这时的 f[1][i+1] 没有算上(因为它包含的仅是第 i+1~len 位的票数和),需要将这一张算到第 i 位 f[0][i] 中:即 f[1][i+1] + (s[i] - '0' + 1)
f[1][i]:(1)当第 i+1 位的选择是1时,同理,在继承 f[0][i+1] 的基础上再算上第 i 位所需的票数 10-x:即 f[0][i+1] + 10 - (s[i] - '0');
(2)当第 i+1 位的选择是2时,同理,由于第 i+1 位还产生了1张第 i 位的票,所以第 i 位的票数为 x+1,用到的票数为10-(x+1):即 f[1][i+1] + 10 - (s[i] - '0' + 1)
最后得到的 f[0][0], f[1][0] 分别表示对于第1位选择1和选择2时,第 1~len 位所有票数的最小值。只需取min(f[0][0],f[1][0])即可。
注意预处理 f[0][len] 和 f[1][len]。
之所以循环i到0结束,是因为最后一个数即第1位如果选择2,向第0位借位的话,所借的这一张票只能第0位那里补算上,所以最后需要的是 f[0][0] 与 f[1][0]。
Code:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define int long long
#define x first
#define y second
//typedef long long LL;
typedef pair<int, int>PII;
const int N = 1e6 + 10;
int f[2][N];
char s[N];
void solve()
{
s[0] = '0';
cin >> (s + 1);
int len = strlen(s + 1); //记录位数
//预处理
f[0][len] = s[len] - '0';
f[1][len] = 10 - (s[len] - '0');
//dp
for (int i = len - 1; i >= 0; i--)
{
f[0][i] = min(f[0][i + 1] + (s[i] - '0'), f[1][i + 1] + s[i] - '0' + 1);
f[1][i] = min(f[0][i + 1] + 10 - (s[i] - '0'), f[1][i + 1] + 10 - (s[i] - '0' + 1));
}
cout << min(f[0][0], f[1][0]) << endl;
}
signed main()
{
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
吐槽:一开始看到大佬的题解是没用dp直接思维感觉还比较好理解,又看到大佬用dp,开始属实是看不懂,看懂后,妙啊,dp一般就是代码更简洁,但思路很重要,细节也是。