USACO Section 1.3 Prime Cryptarithm 解题报告
2016-08-31 19:18
411 查看
题目
题目描述
牛式的定义,我们首先需要看下面这个算式结构:* * * 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\n", 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\n", cnt); } return 0; }
相关文章推荐
- USACO Section1.3 Prime Cryptarithm 解题报告
- USACO Section1.3 Wormholes 解题报告
- USACO Section 1.3 Combination Lock 解题报告
- USACO Section 1.5 Prime Palindromes 解题报告
- USACO Section 1.3 Prime Cryptarithm
- USACO Section1.3 Mixing Milk 解题报告
- USACO Section 1.3 Wormholes 解题报告
- UVA 12542 Prime Substring 解题报告
- USACO 1.3... 虫洞 解题报告(搜索+强大剪枝+模拟)
- USACO Section 1.3 Mixing Milk 解题报告
- USACO Section 1.3 Ski Course Design 解题报告
- USACO 1.3 - Prime Cryptarithm(暴力枚举)
- USACO Section 1.4 Mother's Milk 解题报告
- USACO Section1.3 Combination Lock 解题报告
- USACO Section1.5 Prime Palindromes 解题报告
- USACO - Chapter1 Section 1.3 - Prime Cryptarithm
- Antiprime解题报告
- USACO 1.3-Prime Cryptarithm
- USACO Section 1.5 Number Triangles 解题报告
- USACO 1.3 Prime Cryptarithm