Prime Cryptarithm实现(USACO)
来源:互联网 发布:mac电脑安装flash 编辑:程序博客网 时间:2024/06/15 22:47
1.原题呈现
翻译后的题目如下图所示:
2.实现思路
根据上述原题陈述可知:
要满足的条件有以下几个(在这里我们设“牛式”中的三位数为A,二位数为B):
(1)A中的每一个数需要在给定的数组中选出。
(2)B中的每一个数需要在给定的数组中选出。
(3)B的个位乘以A的得到的数中,每一个数需要在给定的数组中选出,并且是一个三位数。
(4)B的十位乘以A的得到的数中,每一个数需要在给定的数组中选出,并且是一个三位数。
(5)A*B的每一位的数都在数组中选出,并且是一个四位数。
根据上述的要求可以有以下思路:
(1)根据给出的数组找出所有可能的三位数A:n*n*n.
(2)根据给出的数组找出所有可能的二位数B:n*n.
(3)枚举出每一个A*B计算后的结果是否满足上述的五种约束条件,如果满足则“牛式”的总数加1.
3.具体实现
根据上述思路具体实现的代码如下所示:
#include <iostream>#include <algorithm>#include <fstream>using namespace std;int c[10];int check(int );int main(){ int a[10]; int b[1000+5]; int sum1,sum2; int n; ifstream in("crypt1.in",ios::in); ofstream out("crypt1.out",ios::out); in >> n; for(int i = 0; i < n; i++) { in >> a[i]; c[a[i]] = 1; } sort(a,a+n); int p = 0; for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { for(int k = 0; k < n; k++) { b[p] = a[i]*100+a[j]*10+a[k]; p++; } } } int sum = 0; for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { for(int k = 0; k < p; k++) { sum1 = a[i]*b[k]; sum2 = a[j]*b[k]; if(check(sum1)||check(sum2)||check(sum1*10+sum2)) { continue; } if( sum1<1000 && sum1>=100&& sum2<1000 && sum2>=100 && sum1*10+sum2 < 10000 && sum1*10+sum2 >= 1000) { sum++; } else { break; } } } } out << sum << endl; return 0;}int check(int sum){ while(sum) { if(!c[sum%10]) { return 1; } sum = sum / 10; } return 0;}
以下我们详细分析上述代码:
(1)找出由给出的数组组合成的所有可能三位数A,具体试下的代码如下所示,通过三层for()循环实现,并且通过p来记录所有三位数的总数。
for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { for(int k = 0; k < n; k++) { b[p] = a[i]*100+a[j]*10+a[k]; p++; } } }
(2)找出有给出的数组组合成的所有可能两位数B,并将B与每一个三位数进行乘法运算,具体的实现过程如下:
for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { for(int k = 0; k < p; k++) { sum1 = a[i]*b[k]; sum2 = a[j]*b[k]; if(check(sum1)||check(sum2)||check(sum1*10+sum2)) { continue; } if( sum1<1000 && sum1>=100&& sum2<1000 && sum2>=100 && sum1*10+sum2 < 10000 && sum1*10+sum2 >= 1000) { sum++; } else { break; } } } }
在上述代码中,通过一下部分来判定是否满足题目要求的五个约束条件:
if(check(sum1)||check(sum2)||check(sum1*10+sum2)) { continue; } if( sum1<1000 && sum1>=100&& sum2<1000 && sum2>=100 && sum1*10+sum2 < 10000 && sum1*10+sum2 >= 1000) { sum++; }
4.遇到的问题
在实现的过程中,通过枚举的方式去实现,但是没有考虑到“牛式”中数字A分别乘以数字B得到的数的各个位数都满足在数组中。
5.参考的其他思路
可以用用哈希表设计O(1)的穷举法.
两个乘数的位数是固定的,第一个数一定是100到999之间,第二个数只能是10到99之间。
既然如此,那么我们完全没有必要用DFS去按数位搜索,直接穷举100到999间的所有数以及10到99间的所有数。
然后计算乘积与两个分部乘积,判断乘积是否为四位数(是否在1000到9999之间),以及两个分部乘积的位数是否符合要求。
再判断他们是否由给定的数字组成,如果上面的判断都通过了,则计数器加1。
穷举的个数是常数,第一个数有900种可能,第二个数有90种可能,一共有81000种可能。判断是否由给定数字组成的时间复杂度是O(n)。故整个算法的时间复杂度是O(81000n)=O(n)。
然而这还有改进的余地,将可使用数字存在哈希表而不是线性表里。
定义hash[i]:
如果数字i是可以被使用的,则hash[i]=1否则为0。
利用这hash结构,要判断一个固定位数的数是否由给定数字组成,复杂度为O(1)。
故整个算法的时间复杂度也是O(1)。
//代码从网站上摘录#include <cstdio>int n,b[11],k,ans;int hash(int v){ while (v){ if (!b[v%10]) return 0; v/=10; } return 1;}int main(){ freopen("crypt1.in","r",stdin); freopen("crypt1.out","w",stdout); scanf("%d",&n); for (int i(1);i<=n;i++){ scanf("%d",&k); b[k]=1; } for (int i(111);i<1000;i++){ if (hash(i)){ for (int j(11);j<100;j++){ if (i*j<10000 && i*(j/10)<1000 && i*(j%10)<1000 && hash(j) && hash(i*(j%10)) && hash(i*(j/10)) && hash(i*j)) { ans++; // printf("%d * %d = %d0 + %d = %d \n",i,j,i*(j/10),i*(j%10),i*j); } } } } printf("%d\n",ans); return 0;}
6.相关链接
【1】Prime Cryptarithm 原题
【2】Prime Cryptarithm 翻译
- Prime Cryptarithm实现(USACO)
- USACO Prime Cryptarithm Accepted
- Prime Cryptarithm(USACO)
- USACO 1.3-Prime Cryptarithm
- [USACO] Prime Cryptarithm
- USACO 1.3 Prime Cryptarithm
- USACO Prime Cryptarithm
- usaco Prime Cryptarithm
- [USACO]Prime Cryptarithm
- USACO Prime Cryptarithm 杂水题
- USACO:Prime Cryptarithm
- usaco 1.3 Prime Cryptarithm
- USACO Prime Cryptarithm
- USACO-Prime Cryptarithm
- USACO Prime Cryptarithm
- USACO Prime Cryptarithm
- usaco Prime Cryptarithm (暴力)
- USACO 1.3 Prime Cryptarithm
- 安卓开发工具下载
- Android 中的IPC机制的探索(三)
- Android样式的开发:selector篇
- 【杂文】CM 倒下了,但还有这些第三方安卓 ROM 让你刷个痛快
- vim编辑命令总结
- Prime Cryptarithm实现(USACO)
- 算法入门经典2 第6章习题 题解
- <Principles of fMRI 1>课程笔记3--MRI的原理
- JavaBean
- php开启openssl的方法
- 传智播客-Android开发学习随笔-2
- redis持久化RDB和AOF
- 面试题整理
- composer中国镜像安装/配置