首页 > 代码库 > NOIP2002 产生数

NOIP2002 产生数

题目描述

给出一个整数n(n<1030) 和k个变换规则(k<=15)。

规则:

    一位数可变换成另一个一位数:

        规则的右部不能为零。

    例如:n=234。有规则(k=2):

                 2->5

                 3->6

        上面的整数234经过变换后可能产生出的整数为(包括原数):

                 234

                 534

                 264

                 564

        共4种不同的产生数

问题:

  给出一个整数n和k个规则。

求出:

  经过任意次的变换(0次或多次),能产生出多少个不同整数。仅要求输出个数。


输入格式

每个测试文件只包含一组测试数据,每组输入的第一行输入两个整数n和k(n<1030k<=15)。

接下来k行每行输入一个规则,每个规则由两个整数构成。


输出

对于每组输入数据,输出一个整数,表示满足条件的个数。


样例输入

234 2
2 5
3 6

样例输出

4


需要考虑两点

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

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

所以可以通过DFS求每个数可变得其他数的个数


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

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

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int op[10][10];           
char str[105];
int vis[10];
int value[32];
int maxlen=1;
void dfs(int a)         //搜索出每个数可以到达的数的个数   用SS保存
{
    if(vis[a])
        return;
    vis[a]=1;
    ss++;
    for(int i=0;i<=9;i++)
    {
        if(op[a][i]==1)
            dfs(i);
    }
}
void multiple(int x)             //最大可能 9^30 超long long  所有模拟大数乘法
{
    int carry=0;                 //carry表示进位
    for(int i=1;i<=maxlen;i++)
    {
        value[i]=value[i]*x+carry;
            carry=value[i]/10;
            value[i]=value[i]%10;
    }
    if(carry>0)               
        value[++maxlen]=carry;
    return;
}
int main()
{
    int m,n,i,j,k;
    int s;
    value[1]=1;
    memset(op,0,sizeof(op));
    scanf("%s%d",str,&k);
    int a,b;
    if(k==0)               
    {
        cout<<1<<endl;
        return 0;
    }
    while(k--)
    {
        scanf("%d%d",&a,&b);
        op[a][b]=1;               //op[a][b]代表a可以到b
    }
    int len=strlen(str);
    for(i=0;i<len;i++)
    {
        memset(vis,0,sizeof(vis));
        ss=0;
        dfs(str[i]-'0');          //求出每位可转换数的乘积   //其实可以用一个数组保存每个数可转换的数的个数  
        multiple(ss);
    }
    for(i=maxlen;i>=1;i--)
        cout<<value[i];
    cout<<endl;
    return 0;
}




NOIP2002 产生数