bzoj2147: Digit 搜索 贪心优化Dp

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvzelong2014/article/details/83716948

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
f [ i ] [ p 1 ] [ p 2 ] [ p ] f[i][p_1][p_2][p] 表示 i i 位的数,数位和是 p 1 p_1 ,乘 d d 后数位和 p 2 p_2 ,乘 d d 后向下一位进 p p 位是否可行。
这样可以很容易地验证当前答案是否可行。输出的话从高到低位贪心即可。
这样的复杂度是 O ( n s 1 s 2 d ) O(ns_1s_2d) ,显然不能通过。
有一个很巧妙的做法是,考虑倒着做,那么可用的状态实际上是很少的,我们可以结合贪心进行搜索+剪枝。

  • 剪枝1:若剩下的数无法凑足 s 1 s_1 s 2 s_2 可以直接跳过。
  • 剪枝2:当前数如果超过 s 1 s_1 s 2 s_2 可以直接跳过
  • 剪枝3:进位不会超过 d d 位。
  • 如果当前进位 d d ,那么上一位乘 d d 后加上上两位进位一定在 [ d 10 , d 10 + 9 ] [d\cdot10 ,d \cdot 10 + 9] 范围内。

这样的话从最终态进行 D f s Dfs 判断解是否可行即可。复杂度 O ( ) O(能过)
注意由于状态太多,要用 b i t s e t bitset 压位。
这样子跑虽然已经足够优秀,不过是否存在一个 O ( ) O(稳过) 的复杂度呢?
考虑将 ( p 1 , p 2 , p ) (p_1,p_2,p) 抽象成一个点,那么我们要求的实际上就是从 ( s 1 , s 2 , 0 ) (s_1,s_2,0) 恰后走 i i 步到 ( 0 , 0 , 0 ) (0,0,0) ,同时要求走每个数的字典序最小。
这个问题本来是没法在 O ( ) O(点数) 时间内解决的,但是毛爷爷说过,具体问题具体分析嘛。
一个很显然的想法是能不能跑最短路。
考虑如果我们走了 k k 步到达某一个状态 ( p 1 , p 2 , p ) (p_1,p_2,p) 的意义。这意味着有某个 k k 位的数满足数位和是 p 1 p_1 ,乘 d d 后数位和 p 2 p_2 ,乘 d d 后向下一位进 p p 位。那么如果说最小的 k k i i 大,肯定没救了。
那么 k k 小的情况呢?
考虑在最前面补上 0 0 ,那么一定可构造出一组长度 k \ge k ,并且仍然满足条件的解。
于是乎采用 O ( s 1 s 2 d ) O(s_1s_2d) B f s Bfs 即可 O ( 1 ) O(1) 判断任意一个状态是否合法。
然后结合刚才的 D f s Dfs 的方法去贪心输出解,只不过这个时候不用回溯了,因为可以直接判断是否可行进而转移。

代码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;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/83716948
今日推荐