Problem E – Enigma(数位dp)

原题: https://cn.vjudge.net/contest/259142#problem/E

题意:

给一个1000位大数,有些位置用?遮住,给出这个大数的一个1000以内的因子k,求这个大数(多个输出最小的,没有输出*)

解析:

这么经典的数位dp居然没想出来。

d p [ i ] [ j ] i % k = j i i x r i 1 k d p [ i + 1 ] [ j ] d p [ i ] [ ( j + x r ) % k ] = x dp[i][j]表示到第i位为止可能\%k=j的第i位上的最小数\\设第i位上的数为x,r为i位上为1时对k的余数\\显然,dp[i+1][j]合法时,dp[i][(j+x*r)\%k]=x合法

#include<bits/stdc++.h>
using namespace std;
#define LL long long

const LL mod=1e9+7;

char str[1009];
int k;
int remind[1009];
int dp[1009][1009];

int main(){
    scanf("%s%d",str,&k);
    int len=strlen(str);
    remind[len-1]=1%k;
    for(int i=len-2;i>=0;i--){
        remind[i]=remind[i+1]*10%k;
    }

    memset(dp,-1,sizeof(dp));
    dp[len][0]=0;

    for(int i=len-1;i>=0;i--){
        if(str[i]!='?'){
            for(int j=0;j<k;j++){
                if(dp[i+1][j]!=-1)
                    dp[i][(j+(str[i]-'0')*remind[i])%k]=str[i]-'0';
            }
        }
        else{
            for(int j=0;j<k;j++){
                if(dp[i+1][j]!=-1)
                    for(int num=0;num<=9;num++){
                        if(i==0&&num==0)continue;
                        int to=(j+num*remind[i])%k;
                        if(dp[i][to]==-1||dp[i][to]>num)
                            dp[i][to]=num;
                    }
            }
        }
    }
    if(dp[0][0]==-1)printf("*\n");
    else{
        int nex=0;
        for(int i=0;i<len;i++){
            printf("%d",dp[i][nex]);
            nex=((nex-dp[i][nex]*remind[i])%k+k)%k;//上面求dp时的逆运算
        }
        printf("\n");
    }
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/82946660