P1037 产生数(洛谷)
最开始错误的思路是对整条字串进行dfs,看一位dfs进入下一位,这是没有充分理解题意的后果
核心解题思路:不要被这些规则变来变去搞混了,我们需要将原问题进行切割成小的子问题去分析。例如此处我们将每一位数都当做一个独立单位,首先求出在这个位置上有多少种可以存在的数的种类(0~9)。而这些独立单位(位置)互不相同,彼此独立,因此求出总的可变换的数的总数则是每个位置上的数的种类的累乘。对每一位上的数操作时,把变换的规则当做是点与点的连接(一共有10个点位0~9)。因此想要访问所有能走的点(有多少点则有个种类的数),因此这一遍历全部图的思想符合dfs的性质
注意:在数的第一个位置上,我们假设0的存在是合法的,比如0123实际上是123,但它也带来了一个新的数字,因此种类也会增加
实现代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define lll __uint128_t 4 5 char s[100]; //用于储存整数 6 int memory[100]={0}; //用于数有多少种变换种类 实质是一个优化,记忆化储存 7 lll ans; //用于储存数的种类 8 int num=0; //记录每个位置上有多少种类数 9 int flag[100]={0}; //用于dfs中标志 同样直接拿字符'0'~'9'当做下标 10 int k=0; //规则数 11 char a[20],b[20]; //储存变换规则 12 13 void out(lll x){//int128输出要自己写 14 if(x>9)out(x/10); 15 putchar(x%10+48); 16 } 17 void dfs(char index){ 18 flag[index]=1; //走过标记为1 19 num++; //访问到一个点就加一 20 21 for(int i=1;i<=k;i++){ 22 if(index==a[i]){ 23 if(flag[b[i]]) continue; //访问过 24 dfs(b[i]); 25 //这里不能加上回溯,因为不会重复访问同一个点 26 } 27 } 28 return ; 29 30 } 31 int main(){ 32 ios::sync_with_stdio(false); //加快数据的输入和输出 33 cin.tie(0); 34 cout.tie(0); 35 ans=1; //初始化为1 36 37 cin>>s>>k; 38 for(int i=1;i<=k;i++){ 39 cin>>a[i]>>b[i]; 40 } 41 42 for(int i=0;s[i]!='\0';i++){ //对于每一位数去操作 43 num=0; 44 if(memory[s[i]]){ //直接拿ascii码当下标,懒得去转为数字 45 num=memory[s[i]]; //表示之前已经搜过相同的数了,结果存在数组a中 46 }else{ //表示没搜过第一次搜 47 memset(flag,0,sizeof(flag)); 48 dfs(s[i]); 49 memory[s[i]]=num; //储存下来 50 } 51 ans*=num; 52 } 53 out(ans); //输出种类数 54 return 0; 55 }
补充说明:__uint128_t是一个较大的类型,而且符合gcc标准,在很多oj平台上可以使用。但其输出函数不能用cout或者printf,需要自己重写。详细解释见:这里。
如果不用此类型,则需要高精度乘法算法来实现程序。