P5261数字理论题解

题目链接
找数字,一眼数位dp。

考虑设计状态 \(f(k, s, x, p)​\),表示当前在第 \(k​\) 位,数字之和为 \(s​\),这一位给下一位进位为 \(x​\),乘 \(D​\) 后和为 \(p​\)

转移很显然:

\[f(i+1, s+\Delta cur, \lfloor \frac {x + \Delta cur*D}{10}\rfloor,p+(x + \Delta cur*D) \mod 10 ) = f(i+1, s+\Delta cur, \lfloor \frac {x + \Delta cur*D}{10}\rfloor, p+(x + \Delta cur*D) \mod 10 ) or f(i, s, x, p) \]

这个方程可以考虑用bitset优化,因为如果前三个状态相同,对于 \(p\) 是统一进行加操作的。故这里可以通过左移来实现转移。

我们从低位向高位枚举,即可得到每一种方案是否合法。

对于统计答案——

因为要求最小的,故应从高位到低位,每一位都从小到大枚举,判断是否合法。注意,由于乘 \(D\) 后可能变成 \(K+1\) 位数,故对于最高位,其 \(p\) 值不一定为 \(P\)(还可能是 \(P-x\))。

Code:

#include<bits/stdc++.h>

using namespace std;

int K, S, P, D;
int ans[105];
bitset<1023> f[102][1023][10];//P K S 进位
int main(){
    scanf("%d%d%d%d", &K, &S, &P, &D);
    f[0][0][0][0] = 1;
    for(int i = 0; i<K; i++){
        for(int j = 0;j<=i*9; j++){
            for(int k = 0; k<=9; k++){
                for(int cur = (i==(K-1)); cur<=9; cur++){
                    int sum = cur*D+k;
                    f[i+1][cur+j][sum/10]|=(f[i][j][k]<<(sum%10));
                }
            }
        }
    }
    int x, ts, tp, tsum;
    int tmps = S, tmpp = P;
    bool flag = 0;
    for(int i = 0; i<=9; i++){
        if(P>=i&&f[K][S][i][P-i]){//注意,进位后可能为K+1位数xwx
            x = i;
            flag = 1;
            tmpp-=i;
            break;
        }
    }
    if(!flag){
        puts("-1");
    }
    else{
        for(int i = K; i>=1; i--){
            bool flag2 = 0;
            for(int cur = (i==K); cur<=9; cur++){
                for(int px = 0; px<=9; px++){
                    tsum = px+cur*D;
                    ts = tmps-cur;
                    tp = tmpp-(tsum%10);
                    if(ts<0 || tp < 0 || (tsum/10)!=x || !f[i-1][ts][px][tp]){
                        continue;
                    }
                    tmps = ts;
                    tmpp = tp;
                    x = px;
                    ans[i] = cur;
                    flag2 = 1;
                    if(flag2){
                        break;
                    }
                }
                if(flag2){
                    break;
                }
            }
        }
        for(int i = K; i>=1; i--){
            printf("%d", ans[i]);
        }
    }

        system("pause");


    return 0;
}
posted @ 2023-06-07 10:57  霜木_Atomic  阅读(37)  评论(0编辑  收藏  举报