• USACO Section 1.3 Prime Cryptarithm 解题报告


    题目

    题目描述

    牛式的定义,我们首先需要看下面这个算式结构:

          * * *
       x    * *
        -------
          * * *         <-- partial product 1
        * * *           <-- partial product 2
        -------
        * * * *
    

    这是一个乘法结构,我们给出一个数字集合,例如{2,3,5,7},如果我们能够集合里面的数字代替所有的*,使得这个乘法成立的话,那么这就是一个牛式。对于给出的集合,我们需要找出总共有多少个牛式。

    数据范围

    集合中的数字只能是从{1,2,3,4,5,6,7,8,9}中挑选。

    样例输入

    第一行输入所给集合中元素个数,下一行写出集合中的元素

    5
    2 3 4 6 8

    样例输出

    1

    解题思路

    因为数据量不大,所以我直接用枚举的方式,产生每一个被乘数与乘数,然后判断是否它的所有位都是由集合中的元素组合的。枚举出所有的情况,然后统计符合条件的。

    Tip: 我在编码实现的时候遇到一个问题,我在本地测试样例发现可以通过,输出是1,但是当我提交到USACO的判题系统上时,它给我的反馈是我的第一组样例输出为21,我总共提交了十几次,始终不知道是什么原因造成的。后来看了下官方的文档,发现有可能是数组越界...后来思考了很久,终于让我找到了代码的bug!总之,以后编码的时候一定要细心,这种小错误真的很浪费时间,如果真的在比赛中出现这种情况很有可能你就因此与奖牌失之交臂。

    解题代码

    /*
    ID: yinzong2
    PROG: crypt1
    LANG: C++11
    */
    #define MARK
    #include<cstdio>
    #include<set>
    #include<cstdlib>
    #include<algorithm>
    
    using namespace std;
    
    set<int> numSet;
    
    int n, cnt;
    int num[10];
    
    bool test(int x, int length) {
        int a[10];//就是这个数组,最开始的时候我开小了,但是在本地样例可以过。
        int len = 0;
        while(x) {
            a[len++] = x%10;
            x /= 10;
        }
        if(len != length) return false;
        for(int i = 0; i < len; i++) {
            if(numSet.find(a[i]) == numSet.end()) {
                return false;
            }
        }
        return true;
    }
    
    //产生乘数
    void makeMultiplier(int multiplier, int cur, int multiplicand) {
        if(cur >= 2) {
            int firstMul = (multiplier%10) * multiplicand;
            if(test(firstMul, 3)) {//判断是否符条件,应该是一个3位数
                int secondMul = (multiplier/10) * multiplicand;
                if(test(secondMul, 3)) {//同上
                    int sum = secondMul*10 + firstMul;
                    if(test(sum, 4)) {//同上,最后的和是4位的
                        cnt++;
                    }
                }
            }
            return ;
        }
        for(int i = 0; i < n; i++) {
            multiplier += num[i];
            if(0 == cur) {
                multiplier *= 10;
            }
            makeMultiplier(multiplier, cur+1, multiplicand);
            //状态还原
            if(0 == cur) {
                multiplier /= 10;
            }
            multiplier -= num[i];
        }
    }
    
    //产生被乘数
    void makeMultiplicand(int multiplicand, int cur) {
        if(cur >= 3) {
            makeMultiplier(0, 0, multiplicand);
            return ;
        }
        for(int i = 0; i < n; i++) {
            multiplicand += num[i];
            if(cur < 2) {
                multiplicand *= 10;
            }
            makeMultiplicand(multiplicand, cur+1);
            //状态还原
            if(cur < 2) {
                multiplicand /= 10;
            }
            multiplicand -= num[i];
        }
    }
    
    int main() {
    #ifdef MARK
        freopen("crypt1.in", "r", stdin);
        freopen("crypt1.out", "w", stdout);
    #endif // MARK
        while(~scanf("%d", &n)) {
            numSet.clear();
            for(int i = 0; i < n; i++) {
                scanf("%d", &num[i]);
                numSet.insert(num[i]);
            }
            cnt = 0;
            makeMultiplicand(0, 0);
            printf("%d
    ", cnt);
        }
        return 0;
    }
    

    (2017.08.20增加该部分)上面这种写法其实不是很直接,很容易出错。我今天又重新写了一遍,我觉得代码应该以最符合直觉的状态呈现出来,这样就容易读,也容易写。虽然可能会稍微长一点,但是不会出错,基本一遍就能过。

    /*
    ID: yinzong2
    PROG: crypt1
    LANG: C++11
    */
    #define MARK
    #include <iostream>
    #include <cstring>
    
    using namespace std;
    
    int n, cnt;
    int collect[10], num1[5], num2[5], prod1[5], prod2[5], sum[5];
    
    bool judge() {
        for (int i = 0; i < 3; ++i) {
            prod1[i] = num2[0]*num1[i];
            prod2[i] = num2[1]*num1[i];
        }
        prod1[3] = prod2[3] = 0;
        for (int i = 0; i < 3; ++i) {
            prod1[i+1] += (prod1[i]/10);
            prod1[i] = (prod1[i]%10);
            prod2[i+1] += (prod2[i]/10);
            prod2[i] = (prod2[i]%10);
        }
        if (prod1[3] != 0 || prod2[3] != 0) {
            return false;
        }
        sum[0] = prod1[0];
        for (int i = 1; i < 3; ++i) {
            sum[i] = prod1[i] + prod2[i-1];
        }
        sum[3] = prod2[2];
        sum[4] = 0;
        for (int i = 0; i < 4; ++i) {
            sum[i+1] += (sum[i]/10);
            sum[i] = sum[i]%10;
        }
        if (sum[4] != 0) {
            return false;
        }
        for (int i = 0; i < 3; ++i) {
            bool flag = false;
            for (int j = 0; j < n; ++j) {
                if (prod1[i] == collect[j]) {
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                return false;
            }
        }
        for (int i = 0; i < 3; ++i) {
            bool flag = false;
            for (int j = 0; j < n; ++j) {
                if (prod2[i] == collect[j]) {
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                return false;
            }
        }
        for (int i = 0; i < 4; ++i) {
            bool flag = false;
            for (int j = 0; j < n; ++j) {
                if (sum[i] == collect[j]) {
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                return false;
            }
        }
        return true;
    }
    
    void produce2(int cur) {
        if (cur == 2) {
            if (judge()) {
                cnt++;
            }
            return ;
        }
        for (int i = 0; i < n; ++i) {
            num2[cur] = collect[i];
            produce2(cur+1);
        }
    }
    
    void produce(int cur) {
        if (cur == 3) {
            produce2(0);
            return ;
        }
        for (int i = 0; i < n; ++i) {
            num1[cur] = collect[i];
            produce(cur+1);
        }
    }
    
    int main() {
    #ifdef MARK
        freopen("crypt1.in", "r", stdin);
        freopen("crypt1.out", "w", stdout);
    #endif // MARK
        while (cin>>n) {
            for (int i = 0; i < n; ++i) {
                cin >> collect[i];
            }
            cnt = 0;
            produce(0);
            cout << cnt << endl;
        }
        return 0;
    }
    

    解题思路(Type 2)

    思路其实与上述保持一致,但是由于代码不简洁,在参考了大牛的代码之后,我准备重构一下代码。我们在枚举和判断的时候都可以写的更加优美,大牛毕竟是大牛。

    解题代码(Type 2)

    /*
    ID: yinzong2
    PROG: crypt1
    LANG: C++11
    */
    #define MARK
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    
    using namespace std;
    
    int n;
    int numSet[10];
    bool vis[10];
    
    bool ok(int x) {
        while(x) {
            if(!vis[x%10]) return false;
            x /= 10;
        }
        return true;
    }
    
    bool test(int a, int b, int c, int d, int e) {
        int first = a*100 + b*10 + c;
        int mul1 = e * first;
        int mul2 = d * first;
        int sum = mul2 * 10 + mul1;
        if(mul1 < 100 || mul1 > 999) return false;
        if(mul2 < 100 || mul2 > 999) return false;
        if(sum < 1000 || sum > 9999) return false;
        return ok(mul1) && ok(mul2) && ok(sum);
    }
    
    int main() {
    #ifdef MARK
        freopen("crypt1.in", "r", stdin);
        freopen("crypt1.out", "w", stdout);
    #endif // MARK
        while(~scanf("%d", &n)) {
            memset(vis, false, sizeof(vis));
            for(int i = 0; i < n; i++) {
                scanf("%d", &numSet[i]);
                vis[numSet[i]] = true;
            }
            int a,b,c,d,e;
            int cnt = 0;
            for(int i = 0; i < n; i++) {
                a = numSet[i];
                for(int j = 0; j < n; j++) {
                    b = numSet[j];
                    for(int k = 0; k < n; k++) {
                        c = numSet[k];
                        for(int p = 0; p < n; p++) {
                            d = numSet[p];
                            if((a*d) >= 10) continue;//会产生一个四位数,剪枝
                            for(int q = 0; q < n; q++) {
                                e = numSet[q];
                                if((a*e) >= 10) continue;//会产生一个四位数,剪枝
                                if(test(a,b,c,d,e)) {
                                    cnt++;
                                }
                            }
                        }
                    }
                }
            }
            printf("%d
    ", cnt);
        }
        return 0;
    }
    
  • 相关阅读:
    Linux 文件和目录的属性
    关于样式加载顺序,js加载顺序
    关于css切换菜单
    jquery插件编写思路
    京东中关于领券地址的安全处理
    把js写到链接a标签的href中和写到onclick中的区别
    京东中关于jsonp的运用
    关于Jquery中ajax方法data参数用法的总结
    js中的延迟加载
    Jquery版Ajax利用JSONP 跨域POST/GET传输数据研究
  • 原文地址:https://www.cnblogs.com/yinzm/p/5827238.html
Copyright © 2020-2023  润新知