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\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 Section 1.3 Prime Cryptarithm 解题报告的更多相关文章
- USACO Section 1.5 Prime Palindromes 解题报告
题目 题目描述 题目就是给定一个区间[a,b]((5 <= a < b <= 100,000,000)),我们需要找到这个区间内所有既是回文串又是素数的数字. 输入样例 5 500 ...
- USACO Section1.3 Prime Cryptarithm 解题报告
crypt1解题报告 —— icedream61 博客园(转载请注明出处)--------------------------------------------------------------- ...
- USACO Section 1.4 Arithmetic Progressions 解题报告
题目 题目描述 现在给你一个数集,里面的数字都是由p^2+q^2这种形式构成的0 <= p,q <= M,我现在需要你在其中找出一个长为N的等差数列,数列中的第一个数字为a,公差为b,当你 ...
- USACO Section 1.3 Combination Lock 解题报告
题目 题目描述 农夫John的牛从农场逃脱出去了,所以他决定用一个密码锁来把农场的门锁起来,这个密码锁有三个表盘,每个表盘都是环形的,而且上面刻有1~N,现在John设了一个开锁密码,而且这个锁的设计 ...
- USACO Section 1.3 Barn Repair 解题报告
题目 题目描述 某农夫有一个养牛场,所有的牛圈都相邻的排成一排(共有S个牛圈),每个牛圈里面最多只圈养一头牛.有一天狂风卷积着乌云,电闪雷鸣,把牛圈的门给刮走了.幸运的是,有些牛因为放假,所以没在自己 ...
- USACO Section 1.3 Mixing Milk 解题报告
题目 题目描述 Merry Milk Makers 公司的业务是销售牛奶.它从农夫那里收购N单位的牛奶,然后销售出去.现在有M个农夫,每个农夫都存有一定量的牛奶,而且每个农夫都会有自己的定价.假设所有 ...
- USACO Section 1.2 Dual Palindromes 解题报告
题目 题目描述 有一些数(如 21),在十进制时不是回文数,但在其它进制(如二进制时为 10101)时就是回文数. 编一个程序,从文件读入两个十进制数N.S.然后找出前 N 个满足大于 S 且在两种以 ...
- USACO Section 1.2 Palindromic Squares 解题报告
题目 题目描述 输入一个基数B,现在要从1到300之间找出一些符合要求的数字N.如果N的平方转换成B进制数之后是一个回文串,那么N就符合要求.我们将N转换成B进制数输出,然后再将N的平方转换成B进制数 ...
- USACO Section 1.2 Milking Cows 解题报告
题目 题目描述 有3个农夫每天早上五点钟便起床去挤牛奶,现在第一个农夫挤牛奶的时刻为300(五点钟之后的第300个分钟开始),1000的时候结束.第二个农夫从700开始,1200结束.最后一个农夫从1 ...
随机推荐
- Linux Shell 小脚本经典收藏
原文:http://www.cnblogs.com/Javame/p/3867686.html 1.在两个文件中找出相同的号码 diff -y xx.txt oo.txt | egrep -v &qu ...
- Chapter 2 Open Book——7
I gunned my deafening engine to life, ignoring the heads that turned inmy direction, and backed care ...
- hdu_2227_Find the nondecreasing subsequences_树状数组,离散化
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=2227 题意:给你一个集合,让你求递增子序列有多少个,和树状数组求逆序对差不多,不过数据比较大,要离散化 ...
- 25个超有用的 AngularJS Web 开发工具
AngularJS是为了克服HTML在构建应用上的不足而设计的.HTML是一门很好的为静态文本展示设计的声明式语言,改善了JavaScript. 下面我要说的就是25个超有用的AngularJS工具, ...
- Redis和Memcached的区别【转】
如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点:1 Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储.2 Redis支持数据 ...
- 杂音 & pop 音的解决方法
杂音 & pop 音的解决方法 1. 喇叭有严重的"吱吱"破音,绝大多数的原因有可能在于V(out)电压不稳定,所以最好测一下无负载时的输出电压.同时也可以测量 VCC – ...
- 1*Json对象声明简单,复合,对象数组
//简单JSON对象 function btn1_click() { var json = { "id": 1001, "name": "张三&quo ...
- linux 进程命令
Linux中的ps命令是Process Status的缩写.ps命令用来列出系统中当前运行的那些进程.ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的显示进程信 ...
- php 编程效率(1)
用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则 不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的“函数”(译注:PHP手册中 ...
- Android 使用存放在存assets文件夹下的SQLite数据库
因为这次的项目需要自带数据,所以就就把数据都放到一个SQLite的数据库文件中了,之后把该文件放到了assets文件夹下面.一开始打算每次都从assets文件夹下面把该文件夹拷贝到手机的SD卡或者手机 ...