一道很好的构造题。简单来说,给你一个序列 {b}\{b\}{b},bib_ibi 表示 aia_iai 各位数字之和,请还原出序列 {a}\{a\}{a}。有两个附加条件,一是序列 {a}\{a\}{a} 单增,二是 {a}\{a\}{a} 尽可能的小。
对于第二个条件,很容易想到要贪心求解。倘若没有第一个条件,那么显然 bi=9a+b(0<b<9)b_i = 9a+b(0<b<9)bi=9a+b(0<b<9) 的形式是最优的,即还原出的数形如 b99⋯9b99\cdots9b99⋯9。那么有第一个条件时,不难想到根据 bi−1b_{i - 1}bi−1 与 bib_ibi 的大小进行分类讨论。
-
先从 bi−1<bib_{i - 1} < b_ibi−1<bi 这种简单的情况看起。相较于前一个数,只需要把多的部分 bi−bi−1b_i - b_{i - 1}bi−bi−1 按照贪心的思想去填入即可。举个栗子,如果 ai−1=129a_{i - 1} = 129ai−1=129,多出了 888 需要填,那么在 222 上加上 777 补成最大的上限 999,然后将剩余的 8−7=18 - 7 = 18−7=1 填入 111,最后变成了 299299299。
-
再来看 bi−1≥bib_{i - 1} \ge b_ibi−1≥bi 的情况。从低位往高位找到第一个不是 999 的位置,尝试将该位加 111,往高位的方向均不变,往低位的方向重新排列。这里需要注意是否合法(也就是剩余数字之和在减去那些已经确定的数后是否大于等于 000)。当然,往低位的方向的排列方式直接按照贪心的思路即可。若位数不够,则需要补 000,这也是为什么之前判断合法时只需要满足大于等于 000 即可。如果全部遍历完也未出现可以填的,那么只能新开一位并填上 111,然后剩下的按照贪心的思路即可。
在编写代码的时候,分类三种构造的函数。第一种是无条件限制,直接贪心填入。第二种是弱依赖,即需要补充多的部分的第一种情况。第三种便是需要较大改动的第二种情况,其中按照贪心进行填入的部分直接调用第一个函数即可。
完整代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define init(x) memset (x,0,sizeof (x))
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
using namespace std;
const int MAX = 1e5 + 5;
const int MOD = 1e9 + 7;
inline int read ();
int n,a[MAX];
string ans[MAX];
string easy_make (int x);
string medium_make (string la,int x);
string hard_make (string la,int x);
int main ()
{
//freopen (".in","r",stdin);
//freopen (".out","w",stdout);
n = read ();
for (int i = 1;i <= n;++i) a[i] = read ();
for (int i = 1;i <= n;++i)
{
if (i == 1) ans[i] = easy_make (a[i]);
else if (a[i] > a[i - 1]) ans[i] = medium_make (ans[i - 1],a[i]);
else ans[i] = hard_make (ans[i - 1],a[i]);
}
for (int i = 1;i <= n;++i) cout<<ans[i]<<endl;
return 0;
}
inline int read ()
{
int s = 0;int f = 1;
char ch = getchar ();
while ((ch < '0' || ch > '9') && ch != EOF)
{
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9')
{
s = s * 10 + ch - '0';
ch = getchar ();
}
return s * f;
}
string easy_make (int x)//直接贪心填入
{
string s = "";
for (int i = 1;i <= x / 9;++i) s += '9';
x %= 9;
if (x) s = char (x + 48) + s;
return s;
}
string medium_make (string la,int x)
{
int len = la.size ();int sum = x;
string s = "";
for (int i = 0;i < len;++i) sum -= la[i] - '0';
for (int i = len - 1;~i;--i)
{
if (!sum) s = la[i] + s;
else s = char (la[i] + min (9 - (la[i] - '0'),sum)) + s,sum -= min (9 - (la[i] - '0'),sum);//尽量凑成 9
}
return easy_make (sum) + s;
}
string hard_make (string la,int x)
{
int len = la.size ();
for (int i = len - 1;~i;--i)
{
if (la[i] != '9')
{
string s = "";int sum = 0;
for (int j = 0;j < i;++j) s += la[j],sum += la[j] - '0';
s += char (la[i] + 1);sum += la[i] - '0' + 1;
if (x - sum < 0) continue;//要合法才行
string nw = easy_make (x - sum);
int p = len - s.size () - nw.size ();
for (int j = 1;j <= p;++j) s += '0';//补 0 保证 ans_i 单调递增
return s + nw;
}
}
string s = "1";++len;//直接新开一位
string nw = easy_make (x - 1);
int p = len - s.size () - nw.size ();
for (int i = 1;i <= p;++i) s += '0';//补 0 保证 ans_i 单调递增
return s + nw;
}

被折叠的 条评论
为什么被折叠?



