CSP历年复赛题-P1037 [NOIP2002 普及组] 产生数

原题链接:https://www.luogu.com.cn/problem/P1037

题意解读:一个长整数,有若干数字替换规则,计算可以转换成多少种不同的整数。

解题思路:

看题之后,第一感觉,是用DFS:

1、用字符串存储整数

2、用领接表存储数字替换规则,因为一个数字可以替换成多个其他数字

3、在dfs中,枚举字符串每个数字,再枚举规则中每一个可能替换的数字,进行一次替换,继续dfs

隐隐感到不安:30位的整数数量已超过long long,估计要高精度;dfs中对数字判重要借助map,大量的string和dfs可能导致超时、超空间。

不管三七二十一,先写出代码,验证正确性,拿到部分分再说!

40分代码:

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

vector<int> g[10];
int ans;
unordered_map<string, int> h;

void dfs(string s)
{
    if(h[s]) return;
    ans++;
    h[s] = 1;
    for(int i = 0; i < s.size(); i++)
    {
        for(int j = 0; j < g[s[i] - '0'].size(); j++)
        {
            string tmp(s);
            tmp[i] = g[s[i] - '0'][j] + '0';
            dfs(tmp);
        }
    }
}

int main()
{
    string s;
    int k;
    cin >> s >> k;
    int a, b;
    for(int i = 1; i <= k; i++)
    {
        cin >> a >> b;
        g[a].push_back(b); //加入a->b的规则
    }
    dfs(s);
    cout << ans;

    return 0;
}

进一步思考,对于整数的每一个数字,可以替换成不同的数,例如样例:

234 2
2 5
3 6

对于整数234的每一位,

2:可以选2、5,2种选法

3:可以选3、6,2种选法

4:只能选4,1种选法

根据乘法原理,234一共可以生成2 * 2 * 1 = 4种不同的数

到这里,应该能想到,此题的正解是通过排列组合的乘法原理,统计出每一位一共有多少种选法,相乘即可(由于数据量大,需要高精度*低精度)

现在,问题关键在于如何统计每一个数可以替换成多少个不同的数?

映射规则不仅可以是2 5,3 6,还可以有5 1,1 7等等,因此非常类似于图的遍历,用领接表存储后,从0~9每个数字开始,进行DFS遍历,遍历到的每一个不同的数字个数就是起始数字可以替换的数的个数。

ok,下面暂时不考虑高精度问题,实现代码:

60分代码:

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

vector<int> g[10];
int r[10]; //0~9可以替换的数的个数
bool h[10]; //hash,标记数是否已经存在
int cnt;
int ans = 1;

//图的遍历,统计从起点能到达的点的个数
void dfs(int i)
{
    if(!h[i]) cnt++;
    h[i] = 1;
    for(int v : g[i])
    {
        if(!h[v]) dfs(v);
    }
}

int main()
{
    string s;
    int k;
    cin >> s >> k;
    int a, b;
    for(int i = 1; i <= k; i++)
    {
        cin >> a >> b;
        g[a].push_back(b); //加入a->b的规则
    }
   
    for(int i = 0; i <= 9; i++)
    {
        memset(h, 0, sizeof(h)); //清0
        cnt = 0; //清0
        dfs(i);
        r[i] = cnt; //保存i可以替换的数的个数
    }
    
    for(int i = 0; i < s.size(); i++)
    {
        ans *= r[s[i] - '0'];
    }
    cout << ans;
    
    return 0;
}

到这里,离AC又进了一步,保持耐心!下面将计算ans *= r[s[i] - '0']的代码修改为高精度乘法~

100分代码:

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

vector<int> g[10];
int r[10]; //0~9可以替换的数的个数
bool h[10]; //hash,标记数是否已经存在
int cnt;
vector<int> ans(1, 1); //高精度的1

//图的遍历,统计从起点能到达的点的个数
void dfs(int i)
{
    if(!h[i]) cnt++;
    h[i] = 1;
    for(int v : g[i])
    {
        if(!h[v]) dfs(v);
    }
}

//高精度 * 低精度
vector<int> mul(vector<int> a, int b)
{
    vector<int> res;
    int r = 0; //进位
    for(int i = 0; i < a.size(); i++)
    {
        r += a[i] * b;
        res.push_back(r % 10);
        r /= 10;
    }
    while(r)
    {
        res.push_back(r % 10);
        r /= 10;
    }
    return res;
}

int main()
{
    string s;
    int k;
    cin >> s >> k;
    int a, b;
    for(int i = 1; i <= k; i++)
    {
        cin >> a >> b;
        g[a].push_back(b); //加入a->b的规则
    }
   
    for(int i = 0; i <= 9; i++)
    {
        memset(h, 0, sizeof(h)); //清0
        cnt = 0; //清0
        dfs(i);
        r[i] = cnt; //保存i可以替换的数的个数
    }
    
    for(int i = 0; i < s.size(); i++)
    {
        ans = mul(ans, r[s[i] - '0']);
    }
    //高精度,倒序输出
    for(int i = ans.size() - 1; i >= 0; i--)
        cout << ans[i];

    return 0;
}

 

posted @ 2024-05-22 10:58  五月江城  阅读(42)  评论(0编辑  收藏  举报