简述题意,给你一个大数,你可以选择10的次幂进行加减运算,问如何用最少的次数从0到达这个大数
考虑从这个大数到0,从最低位开始,每次都将这个位置取完,2种策略,贪心的话不好处理进位的情况,可以想到是DP
设dp[i][0]为取到第i位,将第i位直接拿完的最小次数,dp[i][1]为取到第i位,进位后拿完的最小次数,可以得到状态转移,num表示第i位的数字
dp[i][0] = min(dp[i-1][0], dp[i-1][1]+1) + num,dp[i-1][1]-1表示进了一位,所以第i位就要+1
dp[i][1] = 10 - num + min(dp[i-1][0], dp[i-1][1]-1) 同理,dp[i-1][1]进了一位,num相当于(num+1), 10-(num+1) = 10 - num - 1
注意初始化状态,最终取答案的时候要在最高位的下一位统计,因为最高位可能也进位了,相当于放了一个虚0
#include<bits/stdc++.h> using namespace std; #define lowbit(x) ((x)&(-x)) typedef long long LL; const int maxm = 1e6+5; int dp[maxm][2]; void run_case() { string str; cin >> str; dp[str.size()-1][1] = str[str.size()-1]-'0'; dp[str.size()-1][0] = 10 - str[str.size()-1] +'0'; for(int i = str.size()-2; i >= 0; --i) { int num = str[i]-'0'; dp[i][1] = min(dp[i+1][0]+1, dp[i+1][1]) + num; dp[i][0] = 10 - num + min(dp[i+1][0]-1, dp[i+1][1]); } cout << min(dp[0][0]+1, dp[0][1]); } int main() { ios::sync_with_stdio(false), cin.tie(0); //cout.setf(ios_base::showpoint);cout.precision(8); run_case(); cout.flush(); return 0; }
我们也可以从最高位开始,dp函数的意义相同,只是状态转移的部分不一样
dp[i][0] = min(dp[i+1][0], dp[i+1][1]) + num
dp[i][1] = 10 - num + min(dp[i+1][0]+1, dp[i+1][1]-1) 第i位进位了,所以dp[i+1][0]要+1, 进了位dp[i+1][1]要-1
#include<bits/stdc++.h> using namespace std; #define lowbit(x) ((x)&(-x)) typedef long long LL; const int maxm = 1e6+5; int dp[maxm][2]; void run_case() { string str; cin >> str; dp[0][0] = str[0] - '0'; dp[0][1] = 10 - str[0] + '0' + 1; for(int i = 1; i < str.size(); ++i) { int num = str[i] - '0'; dp[i][0] = min(dp[i-1][0], dp[i-1][1]) + num; dp[i][1] = 10 - num + min(dp[i-1][0]+1, dp[i-1][1]-1); } cout << min(dp[str.size()-1][0], dp[str.size()-1][1]); } int main() { ios::sync_with_stdio(false), cin.tie(0); //cout.setf(ios_base::showpoint);cout.precision(8); run_case(); cout.flush(); return 0; }
注意代码中的i+1与i-1是相反的,因为字符串读入下标是反的
该题的母题应该是cf gym的一套题