题目描述
题目大意
给定 \(k\) 个规则,规则为“使一位数可变换成另一个一位数”。求整数 \(n\) 根据规则经过若干次(可以为0次或多次)变化,能生成的整数个数。
思路
该题主要考察: Floyd传递闭包,高精度乘法。
显而易见,规则具有传递性。举个例子,1可变换成2,2可变换成3,则1可变换成3。
当然,自己也可以变换成自己。
所以,首先用Floyd传递闭包传递出转换关系。
接着,统计出每个一位数可以变换成的一位数的数量。
最后,运用乘法原理,将整数 \(n\) 每一位的变换可能数相乘即可。
但是,需要注意的有2点:
- \(n < 10^{30}\),所以需要用字符串储存。
- 答案可能过大,需要用高精度乘法进行运算。
代码
#include <bits/stdc++.h>
using namespace std;
string n; // 用字符串储存 n
int k, cnt[15]; // cnt[i] 储存 一位数i的变换可能数
bool a[15][15]; // a[i][j] 表示 i能否变换成j
int ans[1005]; // 记录答案(高精度)
int main()
{
cin >> n >> k;
while (k -- )
{
int u, v; cin >> u >> v;
a[u][v] = 1;
}
// Floyd传递闭包
for (int k = 1; k <= 9; k ++ )
for (int u = 0; u <= 9; u ++ )
for (int v = 1; v <= 9; v ++ )
if (a[u][k] && a[k][v]) a[u][v] = 1;
// 统计 变换可能数
for (int u = 0; u <= 9; u ++ )
{
a[u][u] = 1; // 自己可以变换成自己
for (int v = 0; v <= 9; v ++ )
if (a[u][v]) cnt[u] ++ ;
}
int jw = 0; // 进位变量
ans[0] = 1; // 答案初始值为1,即不进行变换
for (int i = 0; n[i]; i ++ )
{
jw = 0; // 每次重置进位变量
int num = cnt[n[i] - '0']; // 整数n第i位对应的变换可能数
// 高精度乘法,将答案乘以 变换可能数
for (int j = 0; j < 500; j ++ )
{
ans[j] = ans[j] * num + jw;
jw = ans[j] / 10;
ans[j] %= 10;
}
}
// 处理前导0
int i = 500;
while (ans[i] == 0) i -- ;
// 倒序输出
for ( ; i >= 0; i -- ) cout << ans[i];
cout << '\n';
return 0;
}
标签:闭包,15,NOIP,int,题解,2002,变换,Floyd,一位数
From: https://www.cnblogs.com/T-hong/p/18357576