1009 产生数 2002年NOIP全国联赛普及组

1009 产生数

2002年NOIP全国联赛普及组

时间限制: 1 s
空间限制: 128000 KB
题目等级 : 黄金 Gold
 
题目描述 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

数据范围及提示 Data Size & Hint
 

思路:

  符合变换规则的数可以在变换一次后的新数仍然符合变换规则

  所以我们考虑将之转化为一个图论问题

  就是考虑从i到j需要经过多少点

  经过的点的个数就是可以变换成的数

  可是怎么求呢?

  用弗洛伊德算法

  弗洛伊德是个n^3的动态规划

  枚举三个点i,j,k

  如果i到j的距离大于i到k加上k到i的距离就会更新i到j的距离

  根据这个原理我们可以增加一个计数器

  即每更新一次i到j的距离则i的变换数的个数加1

  因为n的本身也算是一种排列

  所以所有数的变换个数初始为1、

  将所有的变换数的个数都求出后

  可以通过相乘的积得出总个数

 
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 long long ans=1,num[10];
 6 int n,map[10][10];
 7 char cur[50];
 8 int main()
 9 {
10     memset(map,127/3,sizeof(map));
11     scanf("%s",cur);
12     scanf("%d",&n);
13     for(int a,b,i=1;i<=n;i++)
14     {
15         scanf("%d%d",&a,&b);
16         map[a][b]=1;
17     }
18     for(int k=0;k<=9;k++)
19     {
20         for(int i=0;i<=9;i++)
21         {
22             for(int j=0;j<=9;j++)
23             {
24                 if(i!=j&&j!=k&&k!=i)
25                 if(map[i][k]+map[k][j]<map[i][j])
26                 {
27                     map[i][j]=map[i][k]+map[k][j];
28                 }
29             }
30         }
31     }
32     for(int i=0;i<=9;i++)
33     {
34         num[i]++;
35         for(int j=0;j<=9;j++)
36         {
37             if(j==i) continue;
38             if(map[i][j]<10000) num[i]++;
39         }
40     }
41     for(int i=0;i<strlen(cur);i++) ans=(ans*num[(int)(cur[i]-'0')]);
42     cout<<ans<<endl;
43     return 0;
44 }

 

posted @ 2017-04-19 20:08  MJT12044  阅读(243)  评论(0编辑  收藏  举报