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)$的最高位再第一/第二种策略下的最优解,那么状态转移方程即为:

  $$
    egin{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*}
  $$

代码如下

 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 }
View Code

分析2(贪心)

  再以个位为例,当个位小于5时,采取第一种策略一定是最优的,而当个位大于5时,采取第二种策略则一定是最优的(即使算上进位对于十位数的影响)。只有当个位等于5时,两种策略的个位收益一致,这时候就要看进位对于十位数的影响了,如果十位数进位后小于5,那就选第一种策略,如果大于5,应该选第二种,等于5,随意。(对于贪心的证明,实在不知道咋证,反正程序是对的。。。)

代码如下

 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 }
View Code
原文地址:https://www.cnblogs.com/zaq19970105/p/12339997.html