Gym 101889E. Enigma (dp => 思维)

题意: 有长度为最多为 1000位的大数,其中某些位是 ?,表示可以是 0 ~ 9 中的任意一个数. 给一个数 n,将所有的 ? 都填上,使得最后满足这个大数是 n 的倍数 (至少1倍),同时最小化这个大数 (不含前导 0)

Analyse:

容易想到的是 dp 做法。dp[i][j] 表示当前到第 i 位,mod n == j 的最小数,转移也并不复杂。

但是问题来了,这个数是一个大数,存不下怎么办。

于是组队的时候来了一个奇特的想法,把这个数当成字符串来写,比较大小的时候直接比较字典序就可以 (到第 i 位的时候长度都一样)。

但是又新增了一个问题,比较字符串字典序的大小 复杂度过不去呀。

然后又来了一种想法:

  • 有一个结构体,结构体包含一个string num,和一个 p . 分别表示到第 i 位时这个数是什么,且 n u m % n = = p num \% n == p num%n==p

  • 有两个 vector,v1 和 v2,都是存结构体

  • v1 中的每个结构体表示到第 i 位,mod n == p 时 最小的数为 num

  • 那么向第 i + 1 位转移时,按 num的值 从小到大 枚举,通过本位的 p 可以算的下一位的 pnext 是多少,如果 pnext 没有出现过,那么直接放进 v2,记录 vis[pnext] = 1。否则不做任何操作 . 处理完一位之后赋值 v1 = v2 即可

  • 解释两点: 虽然是按 num的值 从小到大枚举,但是并不需要排序,因为能保证从一开放入的时候就是从小到大的。每一位的状态最多1000个,因为 n 最多 1000,即 mod n == x,x 的情况最多1000

最后的最后,其实怀疑了一下,因为不知道字符串复制的复杂度,所以抱着侥幸的心试了一试,然后就 A了,大概是 600 ms

具体还是参考代码.

Code:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1010;
bool vis[maxn];
char a[maxn];
int p;
struct Pair{
    string first;
    int second;
};
Pair MP(string a,int b){
    return Pair{a,b};
}
vector<Pair> v1,v2;    //se: mod

int main(){
    scanf("%s %d",a + 1,&p);
    v1.reserve(maxn); v2.reserve(maxn);
    int n = strlen(a + 1);
    if(n > 1) {
        if (a[1] == '?') {
            for (char i = '1'; i <= '9'; ++i) {
                string tmp = ""; tmp += i;
                v1.emplace_back(MP(tmp,(i - '0') % p));
            }
        } else {
            string tmp = ""; tmp += a[1];
            v1.emplace_back(MP(tmp,(a[1] - '0') % p));
        }
        for (int i = 2; i <= n; ++i) {
            for(int i = 0;i < maxn;++ i) vis[i] = 0;
            for(int j = 0;j < v1.size();++ j){
                if(a[i] == '?'){
                    for(char k = '0';k <= '9';++ k){
                        if(!vis[(v1[j].second * 10 + (k - '0')) % p]){
                            v2.emplace_back(MP(v1[j].first + k,(v1[j].second * 10 + (k - '0')) % p));
                            vis[(v1[j].second * 10 + (k - '0')) % p] = 1;
                        }
                    }
                }
                else {
                    if(!vis[(v1[j].second * 10 + (a[i] - '0')) % p]){
                        v2.emplace_back(MP(v1[j].first + a[i],(v1[j].second * 10 + (a[i] - '0')) % p));
                        vis[(v1[j].second * 10 + (a[i] - '0')) % p] = 1;
                    }
                }
            }
            v1 = v2;
            v2.clear();
        }
        int flag = 0;
        for(int i = 0;i < v1.size();++ i){
            if(v1[i].second == 0){
                cout << v1[i].first << endl;
                flag = 1;
                break;
            }
        }
        if(!flag) printf("*\n");
    }
    else {
//        if(a[1] == '?') printf("0\n");
//        else if((a[1] - '0') % p == 0) printf("%c\n",a[1]);
        if(a[1] == '?' && p < 10) printf("%d\n",p);
        else printf("*\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值