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 翻译

0 0