产生数(最短路径)(Floyed)

产生数

题目描述
给出一个整数n(n<10^30)和k个变换规则(k≤15)。
规则:
一位数可变换成另一个一位数:
规则的右部不能为零。
例如:n=234。有规则(k=2):
2->5
3->6
上面的整数234经过变换后可能产生出的整数为(包括原数):
234
534
264
564
共4种不同的产生数
问题:
给出一个整数 n 和k 个规则。
求出:
经过任意次的变换(0次或多次),能产生出多少个不同整数。
仅要求输出个数。
输入格式
键盘输入,格式为:
nk
x1y1
x2y2
… …
xnyn
输出格式
屏幕输出,格式为:
111个整数(满足条件的个数):
输入输出样例
输入
234 2
2 5
3 6
输出
4
分析
这题可以用Floyed算法,参考
最短路径问题(最短路径)(Floyed)
认真分析题目之后发现,本题搜索显然是不行的,而且对于只需计数而不需求具体方案的题目,一般都不会用搜索解决,其实本题不难看出,可以用乘法原理直接进行计数,用Fi表示数字i包括本身可以变成的数字总个数(这里的变成可以是直接变成也可以是间接变成,比如3->5,5->7,那么3->7),那么对于一个数a(用数组存,长度为n),根据乘法原理它能产生出F[a[1]]xF[a[2]]xF[a[3]]x…F[a[n]]个不同整数,相信这一点大家不难理解。那么现在的关键就是如何求Fi,由于这些变换规则都是反应的数字与数字之间的关系,这很容易让我们想到用图来表示这种关系:
1: 建立一个有向图G,初始化g[i, j] False
2: 如果数字i能直接变成数字j,那么g[i, j] True
容易知如果数字i能变成数字j,那么i到j必须存在路径,否则i是不可能变成j的,这样一来,Fi的求解就显得非常简单了,求一个顶点v包括本身能到达的顶点数的方法相当多,可以用BFS,DFS,Dijkstra,Floyd,这里介绍一种类似Floyd的有向图的传递闭包算法,该算法实现简单 ,在解决这类问题时比Floyd效率更高,所谓有向图的传递闭包就是指可达性矩阵A=[a[i, j]],其中
a[i, j] = True 从i到j存在通路
a[i, j] = False 从i到j不存在通路

最后值得注意的是当n很大时输出可能会超过Comp类型的范围,所以要使用高精度乘法,
由于高精度算法是信息学竞赛中的基础,这里就不在详述。

AC代码

#include<iostream>
using namespace std;
int n,o,x,y,k,i1,a[205],f[20][20];
string s;
void c(int k)//高精度
{
	o=0;//清零
	for(int i=200;i>=1;i--)
	{
		a[i]=a[i]*k+o;
		o=a[i]/10;
		a[i]=a[i]%10;
	}
}
int main()
{
	a[200]=1;
	cin>>s>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>x>>y;//输入
		f[x][y]=1;//有向图,只能由这个数到下一个数
	}
	for(int k=0;k<=9;k++)//Floyed变形
	 for(int i=0;i<=9;i++)
	  for(int j=0;j<=9;j++)
		if(f[i][k]==1&&f[k][j]==1)
		 f[i][j]=1;
	for(int i=0;i<=s.size()-1;i++)//每一位枚举
	{
		k=1;
		for(int j=0;j<=9;j++)
		 if(f[s[i]-48][j]==1&&s[i]-48!=j)k++;//如果这一位的数可以转化为另一个数,就++
		c(k);//利用乘法原理
	}
	i1=1;//高精度完了之后的处理
	while(a[i1]==0)i1++;
	for(int j=i1;j<=200;j++)
	 cout<<a[j];
}

谢谢

发布了49 篇原创文章 · 获赞 79 · 访问量 1643

猜你喜欢

转载自blog.csdn.net/weixin_45524309/article/details/103644766