[NOIP 2002普及组]产生数(floyd+高精度)

https://www.luogu.org/problem/P1037

 

 

题目描述

给出一个整数 n(n<10 30) 和 k 个变换规则(k<=15)。
规则:
一位数可变换成另一个一位数:规则的右部不能为零。
例如:n=234。有规则(k=2):
2-> 5
3-> 6
上面的整数 234 经过变换后可能产生出的整数为(包括原数):
234
534
264
564
共 4 种不同的产生数
问题:
给出一个整数 n 和 k 个规则。
求出:
经过任意次的变换(0次或多次),能产生出多少个不同整数。仅要求输出个数。

输入描述:

输入格式为:
n k

x1 y1

x2 y2

... ...

xn yn

输出描述:

一个整数(满足条件的个数)

示例1

输入

234 2
2 5
3 6

输出

 4

 

多给一组数据吧,我第一次写的就这个没过

输入

12345678901234567890 9
2 1
3 2
4 3
5 4
6 5
7 6
8 7
9 8
0 9

输出

13168189440000

 

 

 

 

注意点:

1、一共有15种操作  说明一个数可以变成多个不同的数

如果存在 2个操作 a->b   b->c   则a既可以变到b也可以变到c

所以可以通过DFS或floyd求出每个数字可以变成多少种数字

2、最多可能有30位数 所以最大可能9^30

需要模拟大数乘法 而每次乘数有一个 不超过10 所以并不难

 

故floyd+高精度秒杀,用floyd求出每个数字可以变成多少种数字,然后乘起来

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <iostream>
 4 #include <string>
 5 #include <math.h>
 6 #include <algorithm>
 7 #include <vector>
 8 #include <queue>
 9 #include <set>
10 #include <map>
11 #include <math.h>
12 const int INF=0x3f3f3f3f;
13 typedef long long LL;
14 const int mod=1e9+7;
15 const double PI=acos(-1);
16 const int maxn=100010;
17 using namespace std;
18 //ios::sync_with_stdio(false);
19 //    cin.tie(NULL);
20 
21 int k;
22 char str[50];
23 int tag[10][10];//tag[i][j]=1代表数字i可变成j 
24 int num[10];//存放每个数字可变换数字的个数 
25 int ans[1000];//答案 
26 
27 int main()
28 {
29     scanf("%s %d",str,&k);
30     for(int i=1;i<=k;i++)
31     {
32         int a,b;
33         scanf("%d %d",&a,&b);
34         tag[a][b]=1; 
35     }
36     for(int k=1;k<=9;k++)//floyd 
37     {
38         for(int i=0;i<=9;i++)
39         {
40             for(int j=0;j<=9;j++)
41             {
42                 if(tag[i][k]&&tag[k][j])
43                     tag[i][j]=1;
44             }
45         }
46     }
47     for(int i=0;i<=9;i++)
48     {
49         tag[i][i]=1;
50         for(int j=0;j<=9;j++)
51         {
52             if(tag[i][j])
53                 num[i]++;
54         }
55     }
56     //高精度乘法 
57     ans[0]=1;//初始答案 
58     int jinwei;//进位 
59     int weishu=1;//位数 
60     for(int i=0;str[i];i++)
61     {
62         jinwei=0;
63         int x=num[str[i]-'0'];//该位置数字可变多少种数字 
64         for(int j=0;j<weishu;j++)
65         {
66             int t=ans[j]*x+jinwei;
67             ans[j]=t%10;
68             jinwei=t/10;
69         }
70         while(jinwei)
71         {
72             ans[weishu++]=jinwei%10;
73             jinwei/=10;
74         }
75     }
76     for(int i=weishu-1;i>=0;i--)//注意要反着输出 
77     {
78         printf("%d",ans[i]);
79     }
80     printf("\n");
81     return 0;
82 }

 

 

下面粘另一种写法,主要用了DFS,复杂度可能小一点(from:https://blog.csdn.net/AXuan_K/article/details/41479705)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 int op[10][10];           
 6 char str[105];
 7 int vis[10];
 8 int value[32];
 9 int maxlen=1;
10 void dfs(int a)         //搜索出每个数可以到达的数的个数   用SS保存
11 {
12     if(vis[a])
13         return;
14     vis[a]=1;
15     ss++;
16     for(int i=0;i<=9;i++)
17     {
18         if(op[a][i]==1)
19             dfs(i);
20     }
21 }
22 void multiple(int x)             //最大可能 9^30 超long long  所有模拟大数乘法
23 {
24     int carry=0;                 //carry表示进位
25     for(int i=1;i<=maxlen;i++)
26     {
27         value[i]=value[i]*x+carry;
28             carry=value[i]/10;
29             value[i]=value[i]%10;
30     }
31     if(carry>0)               
32         value[++maxlen]=carry;
33     return;
34 }
35 int main()
36 {
37     int m,n,i,j,k;
38     int s;
39     value[1]=1;
40     memset(op,0,sizeof(op));
41     scanf("%s%d",str,&k);
42     int a,b;
43     if(k==0)               
44     {
45         cout<<1<<endl;
46         return 0;
47     }
48     while(k--)
49     {
50         scanf("%d%d",&a,&b);
51         op[a][b]=1;               //op[a][b]代表a可以到b
52     }
53     int len=strlen(str);
54     for(i=0;i<len;i++)
55     {
56         memset(vis,0,sizeof(vis));
57         ss=0;
58         dfs(str[i]-'0');          //求出每位可转换数的乘积   //其实可以用一个数组保存每个数可转换的数的个数  
59         multiple(ss);
60     }
61     for(i=maxlen;i>=1;i--)
62         cout<<value[i];
63     cout<<endl;
64     return 0;
65 }

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/jiamian/p/11386006.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值