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

题目描述

给出一个整数 \(n(n<10^{30})\) 和 \(k\) 个变换规则 \((k \le 15)\) 。

规则:

一位数可变换成另一个一位数:

规则的右部不能为零。

例如:\(n=234\)。有规则(\(k=2\)):

\(2\)->\(5\)

\(3\)->\(6\)

上面的整数\(234\)经过变换后可能产生出的整数为(包括原数):

\(234\)

\(534\)

\(264\)

\(564\)

共 \(4\) 种不同的产生数

问题:

给出一个整数 \(n\) 和 \(k\) 个规则。

求出:

经过任意次的变换(\(0\)次或多次),能产生出多少个不同整数。

仅要求输出个数。

输入格式

键盘输入,格式为:

\(n\) \(k\)

\(x_1\) \(y_1\)

\(x_2\) \(y_2\)

... ...

\(x_n\) \(y_n\)

输出格式

屏幕输出,格式为:

\(1\) 个整数(满足条件的个数)

样例输入1

234 2
2 5
3 6

样例输出1

4

题解

这个问题我们可以转换成图论里面的问题。

我们可以把 \(0\) 到 \(9\) 这 \(10\) 个数看成 \(10\) 个点。

然后对于任意一对关系 \(x\) -> \(y\) ,我们从 \(x\) 向 \(y\) 连一条边。

那么我们怎么存这个图呢?

图论里面最基础的存图方式是 邻接矩阵邻接表

邻接矩阵 的方法:

我们开一个 \(10 \times 10\) 的数组 \(g[10][10]\) ,

一开始置所有的 \(g[i][j]\) 为 \(false\),

然后如果有一对关系 \(x\) -> \(y\) ,则令 \(g[x][y]\) 为 \(true\)。

这样操作之后,我就知道对于任意一个数 \(x\) ,它能够直接到达的数的个数,即:所有 \(g[x][j]\) 为 \(true\) 的数的个数。

我们这里说的是 \(x\) 到 \(y\) 能直接到达是指 \(x\) 到 \(y\) 有一条直接可达的边(即 \(g[x][y] = true\))。

但是除了直接可达以外,还有间接可达的,比如,如果有两对关系:

\(1\) -> \(2\)

\(2\) -> \(3\)

那么 \(1\) 是可以通过 \(2\) 间接到达 \(3\) 的。

那么怎么确定每一个数可达的数的范围呢?比较方便的形式就是 搜索

我们再开一个bool数组 \(vis[10][10]\) ,\(vis[x][y]\) 用于标记 \(x\) 到 \(y\) 是否可达(包括直接或间接可达);

然后开一个函数 dfs(int u, int s) ,其中 \(u\) 表示当前点, \(s\) 表示起点,如果当前到达点 \(u\) ,则置 \(vis[s][u]\) 为 \(true\),然后对于所有 u 直接可达的点 v ,执行 dfs(v, s)(但是要注意,如果此时 \(vis[s][v]\) 为 \(true\) ,则不需要访问了,因为递归的访问访问过的点会致使函数陷入死循环)。

实现的伪代码如下(伪代码就是不可以编译的代码,但是你可以看懂的代码):

dfs(u, s):
vis[s][u] = true;
for 所有u能够直接到达的v:
if (v没有访问过):
dfs(v, s);

但是这里遇到一个问题,这个问题是邻接矩阵的效率问题,这个问题体现在:

如果现在有 \(10\) 对关系:

\(0\) -> \(1\)

\(1\) -> \(2\)

... ...

\(8\) -> \(9\)

那么如果一开始从 \(0\) 开始搜索,会搜索 \(10\) 层,在每一层,对于当前 dfs(u, s) 中的 \(u\) ,我们需要从 \(0\) 到 \(9\) 遍历 \(v\) ,所以总共需要进行 \(10^{10}\) 次判断。

而这个时间是不允许的(一道题的时间复杂度不能超过 \(10^9\))!

因为邻接矩阵中存在很多多余的判断,对于任意一个 \(u\) ,你需要从 \(0\) 到 \(9\) 去遍历 \(i\) 并判断 \(g[u][i]\) 是否为 \(true\),来确定 \(u\) 到 \(i\) 是否有一条边。这样就有着很多多余的判断。

那么是否能够对于每一个点 \(u\) ,我们都用一个东西记录和它邻接的点(即:它能够到达的点)有哪些呢?

实现的方式有两种:

  1. 链表(链表刚好可以实现这个功能)
  2. 可变数组。

邻接表 的方法:

我们在这里使用 STL (C++标准库)中提供给我们的 vector 容器来实现这个功能。

vector 容器提供给我们的功能就是可变数组的功能。

使用 vector 之前需要添加头文件:

#include <vector>

当然这个头文件也是包含在万能头文件中的。

我们可以通过下面的代码来体会一下 vector 的使用:

#include <bits/stdc++.h>
using namespace std;
vector<int> vec; // 定义一个int类型的可变数组vec
int main() {
for (int i = 1; i <= 5; i ++)
vec.push_back(i); // vec内一次push进去1至5
cout << vec.size() << endl; // vec的大小
for (int i = 0; i < 5; i ++)
cout << vec[i] << ","; // 通过vec[i]获取vec的第i个元素,坐标同样从0开始
return 0;
}

输出结果如下:

5
1,2,3,4,5,

那么我们可以开一个 vector<int> 类型的数组 \(g[10]\) ,然后对于每一对关系 \(x\) -> \(y\) ,执行:

g[x].push_back(y);

然后想要知道点 \(x\) 有哪些直接可达的点 \(y\) ,可以这样遍历:

for (int i = 0; i < g[x].size(); i ++) {
int y = g[x][i];
}

但是以我多年的经验,每次执行 g[x].size() 效率比较低,所以我们可以一开始开一个变量 sz 来存放 g[x].size() ,然后再进行遍历,实现代码如下:

int sz = g[x].size();
for (int i = 0; i < sz; i ++) {
int y = g[x][i];
}

这样我们就大致讲解好了 邻接表 的 vector 实现。

然后我们再开一个 \(cnt\) 数组, \(cnt[x]\) 表示 \(x\) 能够达到的数的数量。

对于我们的样例:

因为 \(2\) 能够到达 \(5\) , \(3\) 能够到达 \(6\) ,所以:

\(cnt[2]=2\)

\(cnt[3]=2\)

其它的 \(cnt\) 值都为 \(1\) 。

所以对于 \(234\) 来说,总的方案数为:

\(cnt[2] \times cnt[3] \times cnt[4] = 2 \times 2 \times 1 = 2\)。

因为数据量达到了 \(10^{30}\) ,用 long long 也存不下,所以得使用高精度乘法(我的代码里面实现了一个很简单的高精度乘法)。

然后我们结合上面讲到的 dfs 函数,可以实现代码如下:

#include <bits/stdc++.h>
using namespace std;
char s[33];
int len, ans[33], k, x, y, cnt[10];
bool vis[10][10];
vector<int> g[10];
void dfs(int u, int s) {
vis[s][u] = true;
int sz = g[u].size();
for (int i = 0; i < sz; i ++) {
int v = g[u][i];
if (!vis[s][v])
dfs(v, s);
}
}
void multi(int a) { // 实现大数和小数的乘法
for (int i = 0; i < 33; i ++)
ans[i] *= a;
for (int i = 0; i < 32; i ++) {
ans[i+1] += ans[i]/10;
ans[i] %= 10;
}
}
void output() { // 输出结果
int i = 32;
while (i > 0 && ans[i] == 0) i --;
while (i >= 0) cout << ans[i--];
cout << endl;
}
int main() {
cin >> s >> k;
while (k --) {
cin >> x >> y;
g[x].push_back(y);
}
for (int i = 0; i < 10; i ++)
dfs(i, i);
for (int i = 0; i < 10; i ++)
for (int j = 0; j < 10; j ++)
cnt[i] += vis[i][j];
ans[0] = 1;
int len = strlen(s);
for (int i = 0; i < len ; i ++)
multi(cnt[ s[i]-'0' ]);
output();
return 0;
}

洛谷P1037 产生数 题解 搜索的更多相关文章

  1. 洛谷P1036 选数 题解 简单搜索/简单状态压缩枚举

    题目链接:https://www.luogu.com.cn/problem/P1036 题目描述 已知 \(n\) 个整数 \(x_1,x_2,-,x_n\) ,以及 \(1\) 个整数 \(k(k& ...

  2. 洛谷 P1037 产生数

    题目描述 给出一个整数n(n<10^30)和k个变换规则(k≤15). 规则: 一位数可变换成另一个一位数: 规则的右部不能为零. 例如:n=234.有规则(k=2): 2->53-> ...

  3. 【洛谷P3960】列队题解

    [洛谷P3960]列队题解 题目链接 题意: Sylvia 是一个热爱学习的女孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有 n×m ...

  4. 洛谷P2507 [SCOI2008]配对 题解(dp+贪心)

    洛谷P2507 [SCOI2008]配对 题解(dp+贪心) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1299251 链接题目地址:洛谷P2507 [S ...

  5. 洛谷P2832 行路难 分析+题解代码【玄学最短路】

    洛谷P2832 行路难 分析+题解代码[玄学最短路] 题目背景: 小X来到了山区,领略山林之乐.在他乐以忘忧之时,他突然发现,开学迫在眉睫 题目描述: 山区有n座山.山之间有m条羊肠小道,每条连接两座 ...

  6. 洛谷P1102 A-B数对

    洛谷P1102 A-B数对 https://www.luogu.org/problem/show?pid=1102 题目描述 出题是一件痛苦的事情! 题目看多了也有审美疲劳,于是我舍弃了大家所熟悉的A ...

  7. 洛谷P2312 解方程题解

    洛谷P2312 解方程题解 题目描述 已知多项式方程: \[a_0+a_1x+a_2x^2+\cdots+a_nx^n=0\] 求这个方程在 \([1,m]\) 内的整数解(\(n\) 和 \(m\) ...

  8. 洛谷P1577 切绳子题解

    洛谷P1577 切绳子题解 题目描述 有N条绳子,它们的长度分别为Li.如果从它们中切割出K条长度相同的 绳子,这K条绳子每条最长能有多长?答案保留到小数点后2位(直接舍掉2为后的小数). 输入输出格 ...

  9. 洛谷P1288 取数游戏II(博弈)

    洛谷P1288 取数游戏II 先手必胜的条件需要满足如下中至少 \(1\) 条: 从初始位置向左走到第一个 \(0\) 的位置,经过边的数目为偶数(包含 \(0\) 这条边). 从初始位置向右走到第一 ...

随机推荐

  1. HZOJ 分组

    打了好多个代码. 对于测试点1,11:手动模拟. void QJ1_11() { ) { int tk; ]+a[]))tk=; ; if(tk<=k) { puts("); puts ...

  2. JAVA之NIO按行读写大文件,完美解决中文乱码问题

    ;//一次读取的字节长度 File fin = new File("D:\\test\\20160622_627975.txt");//读取的文件 File fout = new  ...

  3. 2007年NOIP普及组复赛题解

    题目涉及算法: 奖学金:结构体排序: 纪念品分组:贪心: 守望者的逃离:动态规划: Hanoi 双塔问题:递推. 奖学金 题目链接:https://www.luogu.org/problem/P109 ...

  4. codeforces1217-edu

    C The Number Of Good Substrings 我原来的基本思路也是这样,但是写的不够好 注意算前缀和的时候,字符串起始最好从1开始. #include<cstdio> # ...

  5. Python--day48--ORM框架SQLAlchemy操作表

    ORM框架SQLAlchemy操作表: 表结构和数据库连接: #!/usr/bin/env python # -*- coding:utf-8 -*- from sqlalchemy.ext.decl ...

  6. Tensorflow安装问题: Could not find a version that satisfies the requirement tensorflow pip命令

    引言: Tensorflow大名鼎鼎,这里不再赘述其为何物.这里讲描述在安装python包的时候碰到的“No matching distribution found for tensorflow”,其 ...

  7. 总结:关于留学网站使用laravel框架的总结

    1.从git库中clone后本地项目根目录没有vendor文件夹,安装composer 2.composer install 报错 ,删除 composer.lock 文件,重新执行 composer ...

  8. H3C Hosts文件

  9. linux 阻塞 open 作为对 EBUSY 的替代

    当设备不可存取, 返回一个错误常常是最合理的方法, 但是有些情况用户可能更愿意等待 设备. 例如, 如果一个数据通讯通道既用于规律地预期地传送报告(使用 crontab), 也用于根据 用户的需要偶尔 ...

  10. linux单 open 设备

    提供存取控制的强力方式是只允许一个设备一次被一个进程打开(单次打开). 这个技术最 好是避免因为它限制了用户的灵活性. 一个用户可能想运行不同的进程在一个设备上, 一 个读状态信息而另一个写数据. 在 ...