AtCoder ABC 155E Payment
题目链接:https://atcoder.jp/contests/abc155/tasks/abc155_e
题目大意
有$10^{100}+1$种面值的钞票($1, 10, 10^2, 10^3, \dots, 10^{10^{100}}$),现在有一个价值为$N$的商品,为了买到这个商品,你将付给商家一些钞票,而商家可能会找你一些钞票(如果你实际付款大于$N$的话),请问最少需要多少张钞票即可完成这次交易呢?
分析1(DP)
拿个位举例,假设个位为$X$,那么付清个位只有两种付法,第一种是付$X$张面值为$1$的钞票;第二种是先付一张面值为$10$的钞票,商家再找$10 - X$张面值为$1$的钞票。其余位同理,不同的是,在考虑高位时,如果高位的低一位采取了第二种付法,高位上的数应当加一。以$N = 67$为例,先只使用$1$元钞票搞定$7$元,我先付$10$元,商家再退$3$元,此时$N = 60$,但是我用了一张$10$元钞票,所以商家再退$10$元给我,这样$N = 70$,可以看到,采用第二种策略后十位进了一位,而个位数消耗的钞票为$10 - 7 = 3$张。
由此可以很容易想到可以用DP做,设$X(i) = N \% 10^{i + 1}, 0 \leq i$,设$Y(i)$为$N$从右往左数第$i$个数字,定义$dp[i][0/1]$为$X(i)$的最高位再第一/第二种策略下的最优解,那么状态转移方程即为:
$$
\begin{align*}
dp[i][0] &= min(Y(i) + dp[i - 1][0], Y(i) + dp[i - 1][1]) \\
dp[i][1] &= min(1 + 10 - Y(i) + dp[i - 1][0], 1 + 10 - (Y(i) + 1) + dp[i - 1][1] - 1)
\end{align*}
$$
代码如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 /*-------------------Define Start-------------------*/ 5 typedef bool BL; // 布尔类型 6 typedef char SB; // 有符号1字节,8位 7 typedef unsigned char UB; // 无符号1字节,8位 8 typedef short SW; // 有符号短整型,16位 9 typedef unsigned short UW; // 无符号短整型,16位 10 typedef long SDW; // 有符号整型,32位 11 typedef unsigned long UDW; // 无符号整型,32位 12 typedef long long SLL; // 有符号长整型,64位 13 typedef unsigned long long ULL; // 无符号长整型,64位 14 typedef char CH; // 单个字符 15 typedef float R32; // 单精度浮点数 16 typedef double R64; // 双精度浮点数 17 18 #define Rep(i, n) for (register SDW i = 0; i < (n); ++i) 19 #define For(i, s, t) for (register SDW i = (s); i <= (t); ++i) 20 #define rFor(i, t, s) for (register SDW i = (t); i >= (s); --i) 21 #define foreach(i, c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i) 22 #define ms0(a) memset(a,0,sizeof(a)) 23 #define msI(a) memset(a,0x7f,sizeof(a)) 24 #define LOWBIT(x) ((x)&(-x)) 25 26 #define MP make_pair 27 #define PB push_back 28 #define ft first 29 #define sd second 30 #define ALL(x) x.begin(),x.end() 31 32 #define pr(x) cout << #x << " = " << x << " " 33 #define prln(x) cout << #x << " = " << x << endl 34 35 const ULL mod = 1e9 + 7; //常用模数(可根据题目需要修改) 36 const ULL inf = 0x7fffffff; //用来表示无限大 37 const ULL infLL = 0x7fffffffffffffffLL; //用来表示无限大 38 /*-------------------Define End-------------------*/ 39 40 const UDW maxN = 1e6 + 7; 41 string N; 42 SDW dp[maxN][2]; 43 SDW ans; 44 45 void input(){ 46 cin >> N; 47 } 48 49 void solve(){ 50 dp[1][0] = N[N.size() - 1] - '0'; 51 dp[1][1] = 11 - N[N.size() - 1] + '0'; 52 53 For(i, 2, N.size()) { 54 SDW Y = N[N.size() - i] - '0'; 55 56 dp[i][0] = min(Y + dp[i - 1][0], Y + dp[i - 1][1]); 57 dp[i][1] = min(1 + 10 - Y + dp[i - 1][0], 1 + 10 - (Y + 1) + dp[i - 1][1] - 1); 58 } 59 60 ans = min(dp[N.size()][0], dp[N.size()][1]); 61 } 62 63 void output(){ 64 cout << ans << endl; 65 } 66 67 int main() { 68 input(); 69 solve(); 70 output(); 71 return 0; 72 }
分析2(贪心)
再以个位为例,当个位小于5时,采取第一种策略一定是最优的,而当个位大于5时,采取第二种策略则一定是最优的(即使算上进位对于十位数的影响)。只有当个位等于5时,两种策略的个位收益一致,这时候就要看进位对于十位数的影响了,如果十位数进位后小于5,那就选第一种策略,如果大于5,应该选第二种,等于5,随意。(对于贪心的证明,实在不知道咋证,反正程序是对的。。。)
代码如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 /*-------------------Define Start-------------------*/ 5 typedef bool BL; // 布尔类型 6 typedef char SB; // 有符号1字节,8位 7 typedef unsigned char UB; // 无符号1字节,8位 8 typedef short SW; // 有符号短整型,16位 9 typedef unsigned short UW; // 无符号短整型,16位 10 typedef long SDW; // 有符号整型,32位 11 typedef unsigned long UDW; // 无符号整型,32位 12 typedef long long SLL; // 有符号长整型,64位 13 typedef unsigned long long ULL; // 无符号长整型,64位 14 typedef char CH; // 单个字符 15 typedef float R32; // 单精度浮点数 16 typedef double R64; // 双精度浮点数 17 18 #define Rep(i, n) for (register SDW i = 0; i < (n); ++i) 19 #define For(i, s, t) for (register SDW i = (s); i <= (t); ++i) 20 #define rFor(i, t, s) for (register SDW i = (t); i >= (s); --i) 21 #define foreach(i, c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i) 22 #define ms0(a) memset(a,0,sizeof(a)) 23 #define msI(a) memset(a,0x7f,sizeof(a)) 24 #define LOWBIT(x) ((x)&(-x)) 25 26 #define MP make_pair 27 #define PB push_back 28 #define ft first 29 #define sd second 30 #define ALL(x) x.begin(),x.end() 31 32 #define pr(x) cout << #x << " = " << x << " " 33 #define prln(x) cout << #x << " = " << x << endl 34 35 const ULL mod = 1e9 + 7; //常用模数(可根据题目需要修改) 36 const ULL inf = 0x7fffffff; //用来表示无限大 37 const ULL infLL = 0x7fffffffffffffffLL; //用来表示无限大 38 /*-------------------Define End-------------------*/ 39 40 const UDW maxN = 1e6 + 7; 41 string N; 42 SLL ans; 43 44 void input(){ 45 cin >> N; 46 N = "0" + N; 47 } 48 49 void solve(){ 50 rFor(i, N.size() - 1, 0) { 51 SLL x = N[i] - '0'; 52 53 if(x > 5 || x == 5 && N[i - 1] >= '5') { 54 ans += 10 - x; 55 ++N[i - 1]; 56 } 57 else { 58 ans += x; 59 } 60 } 61 } 62 63 void output(){ 64 cout << ans << endl; 65 } 66 67 int main() { 68 input(); 69 solve(); 70 output(); 71 return 0; 72 }