《产生数》解题报告
by MPS
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
题目描述 Description
给出一个整数 n(n<10^30) 和 k 个变换规则(k<=15)。
规则:
一位数可变换成另一个一位数:
规则的右部不能为零。
例如:n=234。有规则(k=2):
2-> 5
3-> 6
上面的整数 234 经过变换后可能产生出的整数为(包括原数):
234
534
264
564
共 4 种不同的产生数
问题:
给出一个整数 n 和 k 个规则。
求出:
经过任意次的变换(0次或多次),能产生出多少个不同整数。
仅要求输出个数。
输入描述 Input Description
键盘输人,格式为:
n k
x1 y1
x2 y2
... ...
xn yn
输出描述 Output Description
屏幕输出,格式为:
一个整数(满足条件的个数)
样例输入 Sample Input
234 2
2 5
3 6
样例输出 Sample Output
4
数学建模:将一个数码抽象为点,能够变换抽象为边,那么很明显,我们对整个数字串扫描,如果遇到可以变换的,则乘上他的变化数目,这里之所以用乘是因为组合数学里的乘法原理。而求变换数目,则就是著名的传递闭包算法——Floyd_Warshall算法
我们将能连通的两条边的权值看为true,然后再类似floyd的模型,疏通整个图的关系,保存到一个rule数组里去,然后再遍历即可。
上述算法期望得分:60分?!
为什么呢?
很明显,变换之积可能爆了精度,所以我们要用高精度。高乘单,这里MPS又加了一个优化——快速傅里叶变换,不过给的程序里就不会那么繁琐了,能过就行了
代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
bool map[10][10];
int rules[10];
int ans[1000000];
int len,n,k;
char s[100];
int main()
{
freopen("creat.in","r",stdin);
freopen("creat.out","w",stdout);
int i,j,k,x,y;
cin>>s>>k;
for(i=1;i<=k;i++){
cin>>x>>y;
map[x][y]=1;
}
for (int i=0;i<10;++i) map[i][i]=true;
for(k=0;k<=9;k++)
for(i=0;i<=9;i++)
for(j=0;j<=9;j++)
map[i][j]=map[i][j]||(map[i][k] && map[k][j]);
for(i=0;i<=9;i++)
for(j=0;j<=9;j++)
if(map[i][j])++rules[i];
ans[1]=len=1;
for (int i=0;i<strlen(s);++i)
{
int x=rules[s[i]-'0'];
for (int j=1;j<=len;++j) ans[j]*=x;
for (int j=1;j<=len;++j)
{
ans[j+1]+=ans[j]/10;
ans[j]%=10;
}
while (ans[len])
{
ans[len+1]+=ans[len]/10;
ans[len]%=10;
++len;
}
}
for (int i=len-1;i>0;--i) printf("%d",ans[i]);
return 0;
}
------------------------------------------------------------------------------------------------------the end----------------------------------------------------------------------------------------------------