bzoj2147: Digit
Description
在数学课上,小T又被老师发现上课睡觉了。为了向全班同学证明小T刚才没有好好听课,数学老师决定出一道题目
刁难一下小T,如果小T答不出,那么……情节就按照俗套的路线发展下去了,小T显然无法解决这么复杂的问题,
可怜的小T只能向你求助:题目是这样的:求一个满足条件的n位数A(不能有前导0),满足它的数字和为s1,并且,A
*d的数字和为s2.
Input
一行四个整数:n, s1, s2, d
1≤n≤100,0≤s1≤n*9,0≤s2≤(n+1)*9,0≤d≤9
Output
若存在最小的满足条件的数,则输出这个数,否则输出-1。
Sample Input
2 9 9 5
Sample Output
18
【样例说明】
1+8=9
18*5=90
9+0= 9
分析
我觉得小T活该,可怜之人必有可恨之处
首先可以很容易地想出一个Dp
表示
位的数,数位和是
,乘
后数位和
,乘
后向下一位进
位是否可行。
这样可以很容易地验证当前答案是否可行。输出的话从高到低位贪心即可。
这样的复杂度是
,显然不能通过。
有一个很巧妙的做法是,考虑倒着做,那么可用的状态实际上是很少的,我们可以结合贪心进行搜索+剪枝。
- 剪枝1:若剩下的数无法凑足 或 可以直接跳过。
- 剪枝2:当前数如果超过 或 可以直接跳过
- 剪枝3:进位不会超过 位。
- 如果当前进位 ,那么上一位乘 后加上上两位进位一定在 范围内。
这样的话从最终态进行
判断解是否可行即可。复杂度
注意由于状态太多,要用
压位。
这样子跑虽然已经足够优秀,不过是否存在一个
的复杂度呢?
考虑将
抽象成一个点,那么我们要求的实际上就是从
恰后走
步到
,同时要求走每个数的字典序最小。
这个问题本来是没法在
时间内解决的,但是毛爷爷说过,具体问题具体分析嘛。
一个很显然的想法是能不能跑最短路。
考虑如果我们走了
步到达某一个状态
的意义。这意味着有某个
位的数满足数位和是
,乘
后数位和
,乘
后向下一位进
位。那么如果说最小的
比
大,肯定没救了。
那么
小的情况呢?
考虑在最前面补上
,那么一定可构造出一组长度
,并且仍然满足条件的解。
于是乎采用
的
即可
判断任意一个状态是否合法。
然后结合刚才的
的方法去贪心输出解,只不过这个时候不用回溯了,因为可以直接判断是否可行进而转移。
代码1
跑了快3s
#include<bits/stdc++.h>
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
const int N = 1e9;
std::bitset<N>vis;
int n, s1, s2, d, a[101];
void Dfs(int i, int p1, int p2, int p) {
if(p1 > s1 || p2 > s2) return ;
if((n - i + 1) * 9 + p1 < s1) return ;
if((n - i + 1) * 9 + p2 < s2) return ;
if(p > d) return ;
int id = p + p2 * 11 + p1 * 9999 + i * 8999100;
if(vis[id]) return ;
vis[id] = true;
if(i > n) {
if(p1 == s1 && p2 == s2 && !p) {
for(int i = 1;i <= n; ++i) printf("%d", a[i]);
puts(""); exit(0);
}
return ;
}
for(int j = p * 10; j <= p * 10 + 9; ++j) {
int x = j / d, np = j - x * d;
a[i] = x; Dfs(i + 1, p1 + x, p2 + j % 10, np);
}
}
int main() {
n = ri(); s1 = ri(); s2 = ri(); d = ri();
for(int i = 1;i <= 9; ++i)
for(int p = 0;p <= d - 1; ++p)
a[1] = i, Dfs(2, i, (i * d + p) % 10 + (i * d + p) / 10, p);
puts("-1");
return 0;
}
代码2
跑了快1s左右
#include<bits/stdc++.h>
const int N = 8199101;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int f[901][910][10], a[101], n, d, s1, s2;
struct data {int x, y, z;}q[N];
bool can(int x, int y, int z, int st) {return ~f[x][y][z] && f[x][y][z] <= st;}
void Dfs(int st, int p1, int p2, int p) {
if(st == 0) {
for(int i = n; i; --i) printf("%d", a[i]);
puts(""); exit(0);
}
for(int j = p * 10;j <= p * 10 + 9; ++j) {
int x = j / d, np = j - x * d;
if(can(p1 - x, p2 - (j % 10), np, st - 1))
a[st] = x, Dfs(st - 1, p1 - x, p2 - (j % 10), np);
}
}
int main() {
memset(f, -1, sizeof(f));
n = ri(); s1 = ri(); s2 = ri(); d = ri();
int L, R; f[0][0][0] = 0; q[L = R = 1] = (data) {0, 0, 0};
for(data u = q[1]; L <= R; u = q[++L]) {
for(int i = 0;i <= 9; ++i) {
int x = u.x + i, y = i * d + u.z, z = y / 10; y %= 10; y += u.y;
if(x <= s1 && y <= s2 && !~f[x][y][z])
q[++R] = (data) {x, y, z}, f[x][y][z] = f[u.x][u.y][u.z] + 1;
}
}
for(int i = 1;i <= 9; ++i) {
for(int z = 0;z <= d - 1; ++z) {
int x = i * d + z, y = x / 10; x %= 10;
if(can(s1 - i, s2 - x - y, z, n - 1))
a[n] = i, Dfs(n - 1, s1 - i, s2 - x - y, z);
}
}
puts("-1");
return 0;
}