atcoder arc123 C题 1, 2, 3 - Decomposition

1|0题目大意

给出 N , 求出最少的数满足每个数位上只为 1,2,3 且所有数的和为 N

2|0题解

首先, 我们可以有一个结论: 对于任意两个数位, 当 i>j, 则数位大于等于 i 的数的个数不多于数位大于等于 j 的数的个数。
证明: 若一个数, 它的数位大于等于 i , 因为 i>j , 所以它的数位一定大于等于 j
有了这个性质, 接下来再考虑题怎么做。
看到这里的最少个数, 比较好想到的应该是二分, 因为它貌似好像有单调性。 但仔细观察后你会发现, 这东西压根就不是单调的。
比如 N=1 时, 只能用一个数凑出来, 多一个少一个都不行, 那么我们就只好换一种方法了。
既然刚才推出来的结论和位数有关, 那我们是不是该用这个结论做些什么呢? 比如, 用每位数上是几去做一个 DP
首先考虑状态, 既然我们要个数尽量少, 那么就需要每一个数位状态上的数尽量少, 而且我们还要考虑可以进位的情况, 所以定义状态为 dp[i][j] 表示在 i1 位进位小于等于 9 可以使 i 位及之前的数与 N 中相同位置的数相同 , 且 i 位为 j 的时候的最少个数。 我们考虑可以通过全填 1 来算出能不能用至少 k1 ~ 3 来凑出 M 。而我们也可以用 M/3 来算出没有限制时最少要用多少个 1 ~ 3 来凑出 M 。 那么, 就可以得到函数:

long long f (int n//要凑出的数, int h//至少要用的数) { if (h > n) {//如果h全填1都大于n了, 那不可能填出来 return INF; } if (n == 0) {//如果n是0就可以直接凑出来, 其实这个判断根本没必要, 可以不加 return 0; } return max ((n + 2) / 3, h);//求出至少要多少可以凑出来n }

对于每个数位, 我们都可以通过前一个数还剩下的与自己本身这一位规定组成一个数, 这就是我们要在个数大于等于上一次的个数这一限制下需要凑出的数。
最后, 我们就可以定义初始状态了。
因为 N 的最高位没有进位, 所以对于 N 的最高位 x , 所有大于 x 的状态都不存在, 设为 INF , 其它的直接用 f() 构造。

代码:

#include <cstdio> #include <algorithm> using namespace std; #define MAXN 18 #define INF 0x3f3f3f3f int s[MAXN + 5]; long long s1[MAXN + 5][10 + 5]; long long f (int n, int h) { if (h > n) { return INF; } return max ((n + 2) / 3, h); } int main () { int t; scanf ("%d", &t); while (t --) { long long n; int tot = 0; scanf ("%lld", &n); while (n) { s[++tot] = n % 10; n /= 10; } for (int i = s[tot]; i >= 0; i --) { s1[tot][i] = f (i, 0); } for (int i = 9; i > s[tot]; i --) { s1[tot][i] = INF; } for (int i = tot - 1; i >= 1; i --) { for (int k = 0; k <= s[i]; k ++) { s1[i][k] = INF; for (int j = 0; j <= s[i + 1]; j ++) { s1[i][k] = min (s1[i][k], f ((s[i + 1] - j) * 10 + k, s1[i + 1][j])); } for (int j = s[i + 1] + 1; j <= 9; j ++) { s1[i][k] = min (s1[i][k], f ((s[i + 1] + 10 - j) * 10 + k, s1[i + 1][j])); } } for (int k = s[i] + 1; k <= 9; k ++) { s1[i][k] = INF; for (int j = 0; j < s[i + 1]; j ++) { s1[i][k] = min (s1[i][k], f ((s[i + 1] - j - 1) * 10 + k, s1[i + 1][j])); } for (int j = s[i + 1]; j <= 9; j ++) { s1[i][k] = min (s1[i][k], f ((s[i + 1] + 10 - j - 1) * 10 + k, s1[i + 1][j])); } } } printf ("%lld\n", s1[1][s[1]]); } }

__EOF__

本文作者FlowerDream
本文链接https://www.cnblogs.com/flower-dream/p/1-2-3-Decomposition_Answer.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   小篪篪  阅读(117)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
Live2D
欢迎阅读『atcoder arc123 C题 1, 2, 3』
点击右上角即可分享
微信分享提示