本文正在参与掘金团队号上线活动,点击 查看大厂春招职位
一、题目描述:
今天要写的题来自 Codeforces,也是一道比较有意思的题。
Codeforces 1513C 加一:
题目大意:
给你一个数字串 ,每次操作都需要对其中的每一位数字加一,数字 变成 ,…, 数字 变成 ,比如 会变成 ,后面的操作也要遵循这个规则。
输入:
第一行是测试样例数 。
之后的 行,每一行都包括一个初始数字 和一个操作步数 。
输出:
你需要对每一个样例输出进行 次操作后数字串的长度 后的结果。
二、思路分析:
经过简单的分析可以发现 的数字只要经过足够的步数后都会变为 ,而后的问题就变成了两个规模更小的问题: 和 在经过剩下的步数后会变成多少位。
我们可以用 , 表示 在 步后的长度,很容易可以看出 在 步后会变成 ,所以之后的情形可以简单地等效于 和 在经过 步后的总长度是多少。
所以有
看到这里,想必你已经知道怎么写了吧,预处理出一个二维数组记录可能会用到的 值,这就是二维 DP。但是做到这里我们还可以将其优化成一维。
设 表示 在 步后的长度,由于 都可以看作 经过 步后产生的,所以 。
令 ,再结合上面的推导,我们就可以得到:
三、AC 代码:
#include <iostream>
using namespace std;
using ll = long long;
const int maxn = 2e5 + 50;
const int mod = 1e9 + 7;
int dp[maxn];
void getDp() {
for (int i = 0; i < 10; ++i) {
dp[i] = 1;
}
for (int i = 10; i < maxn; ++i) {
dp[i] = (dp[i - 10] + dp[i - 9]) % mod;
}
}
void solve() {
int n, m;
cin >> n >> m;
int ans = 0;
while (n) {
ans = ((ll)ans + dp[n % 10 + m]) % mod;
n /= 10;
}
printf("%d\n", ans);
}
int main() {
// 一定要关闭同步流,不然 cin 会超时
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
getDp();
int t;
cin >> t;
while (t--) {
solve();
}
}
复制代码
四、总结:
在写这道题的时候我首先想到的是倒推的递归写法,和 DP 写法的区别在于一个是从终点开始往前推,一个是从 开始往后推。这两种写法的思想都是将大问题分解成小问题来解决,也就是分治,再一次说明了基本思想的重要性。简单的递归可能会导致栈溢出,感兴趣的同学可以尝试使用记忆化搜索优化来写递归,但那就是另外一个故事了。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END