信息学奥赛一本通 1920:【02NOIP普及组】产生数 | 洛谷 P1037 [NOIP2002 普及组] 产生数

【题目链接】

ybt 1920:【02NOIP普及组】产生数
洛谷 P1037 [NOIP2002 普及组] 产生数

【题目考点】

1. 动态规划

2. 高精度

【解题思路】

解法1:动态规划

首先通过搜索,得到每一位数字通过有限次变化后可能变成的数字种类。
得到数组c,c[i]表示数字i经过有限次变化后可以变成的数字种类数。

假设有规则:1->2, 1->3, 2->5, 5->1。
那么数字1经过有限次变化后可以成为1,2,3,5,共4种;2经过有限次变化后可以成为:2,5,1,3,共4种。

1. 状态定义

集合:数字n通过给定规则可以变成的数字
限制:只看前几位
统计量:方案数
dp[i]:前i位数字在应用规则经过有限次变化后可以变成的数字种类数。
初始状态:前0位数字的种类数可以认为是1,即dp[0] = 1

2. 状态转移方程

前i位数字通过应用规则可以变成的数字
分割结合:根据第i位数字变成其它数字的情况分割集合
已知第i位数字a[i]可以变为c[a[i]]种数字。
那么前i位数字可以变成的数字种类数,为前i-1位数字可以变成的数字种类数dp[i-1]乘以第i位数字a[i]可以变成的数字种类数c[a[i]],即dp[i] = dp[i-1]*c[a[i]]

由于1个数字最多可以变为10个数字(包括变为自己),最多有30位,根据这一状态转移方程,结果可能会是30个10相乘,低精度数字无法表示。因此数字n,状态dp需要用高精度数字来表示,状态转移方程要用到高精乘低精。

【题解代码】

解法1:

#include<bits/stdc++.h>
using namespace std;
#define K 20
#define N 35
struct HPN
{
    
    
    int a[N];
    HPN()
    {
    
    
        memset(a, 0, sizeof(a));
    }
    HPN(string s)
    {
    
    
        memset(a, 0, sizeof(a));
        a[0] = s.length();
        for(int i = 1; i <= a[0]; ++i)
            a[i] = s[a[0]-i] - '0';
    }
    int& operator [] (int i)
    {
    
    
        return a[i];
    }
    void setLen(int i)
    {
    
    
        while(a[i] == 0 && i > 1)
            i--;
        a[0] = i;
    }
    HPN operator * (int b)//高精乘低精 
    {
    
    
        HPN r;
        int c = 0, i;
        for(i = 1; i <= a[0]; ++i)
        {
    
    
            r[i] = a[i] * b + c;
            c = r[i] / 10;
            r[i] %= 10; 
        }
        while(c > 0)
        {
    
    
            r[i++] = c % 10;
            c /= 10;
        }
        r.setLen(i);
        return r;
    }
    void show()
    {
    
    
        for(int i = a[0]; i >= 1; --i)
            cout << a[i];
        cout << endl;
    }
};
int k, c[10], vis[10], x[K], y[K];
HPN dp[N], n;
void dfs(int i)
{
    
    
    for(int j = 1; j <= k; ++j)
    {
    
    
        if(x[j] == i && vis[y[j]] == false)
        {
    
    
            vis[y[j]] = true;
            dfs(y[j]);
        }
    }
}
void init()
{
    
    
    for(int i = 0; i <= 9; ++i)
    {
    
    
        memset(vis, 0, sizeof(vis));//vis[j]:i能否通过应用某些规则变成数字j 
        vis[i] = true;
        dfs(i);//标记vis 
        for(int j = 0; j <= 9; ++j)//统计vis中有几个数字被标记 
        {
    
    
            if(vis[j])
                c[i]++;//c[i]:数字i能变成的数字的个数(包括自己) 
        }
    }
}
int main()
{
    
    
    string s;
	cin >> s >> k;
	for(int i = 1; i <= k; ++i)
		cin >> x[i] >> y[i];
	init();
	n = HPN(s);
    dp[0] = HPN("1");
    for(int i = 1; i <= n[0]; ++i)//n[0]为数字n的长度
        dp[i] = dp[i-1] * c[n[i]];
    dp[n[0]].show();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/lq1990717/article/details/125511944