USACO Section 1.3 Combination Lock 解题报告
2016-09-12 00:08
316 查看
题目
题目描述
农夫John的牛从农场逃脱出去了,所以他决定用一个密码锁来把农场的门锁起来,这个密码锁有三个表盘,每个表盘都是环形的,而且上面刻有1~N,现在John设了一个开锁密码,而且这个锁的设计者也有一个开锁密码。这个锁有一个特点,那就是它的容错方式,当我们输入密码时,如果我们的密码与正确的开锁密码相比较,对应的每一个表盘上的数字的距离都相差不超过2时,这个密码便可以打开这个锁。例如:John设定的开锁密码为
(1,2,3),锁的设计者的开锁密码为
(4,5,6)。如果我们现在将锁设定为
(1,3,5),那么这个密码也可以将这个锁打开,因为我们的密码相对于John的密码每一个表盘上的数字的距离分别为
0, 1, 2,没有超过2的距离,所以可以打开。如果我们的密码是
(2,4,8),那么同样这个密码对于锁设计者的密码来说,每个表盘上的数字距离分别为
2,1,2,所以这个密码也是可以打开的。但是如果我们的密码是
(1,5,6),这个密码对于John的密码距离分别为
0,3,3,对于设计者的密码距离分别为
3,0,0,都存在距离超过2的情况,所以这个密码无法打开这把锁。
现在我们设定一个N,然后输入John的密码与设计者的密码,需要我们计算出有多少种密码是可以打开这把锁的。
数据范围
1 <= N <= 100
样例输入
50
1 2 3
5 6 7
样例输出
249
解题思路
我们用最暴力的方法是可以解决这个问题的,因为这个题目的数据量不是很大,我们可以枚举每一种可能的密码,然后一一判断这个密码是否能够打开这把锁。当然这种方法的时间复杂度是比较高的,对于数据量比较大的时候可能不合适。解题代码
/* ID: yinzong2 PROG: combo LANG: C++11 */ #define MARK #include<cstdio> #include<cstring> #include<cstdlib> using namespace std; const int MAXN = 100+10; int n; int farmer[3], master[3]; bool vis[MAXN][MAXN][MAXN]; bool ok(int a, int b) { if(abs(a-b) <= 2 || abs(a-b) >= n-2) return true; return false; } int main() { #ifdef MARK freopen("combo.in", "r", stdin); freopen("combo.out", "w", stdout); #endif // MARK while(~scanf("%d", &n)) { memset(vis, false, sizeof(vis)); for(int i = 0; i < 3; i++) { scanf("%d", &farmer[i]); } for(int i = 0; i < 3; i++) { scanf("%d", &master[i]); } int ans = 0; for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { for(int k = 1; k <= n; k++) { if(!vis[i][j][k]) { if((ok(i, farmer[0]) && ok(j, farmer[1]) && ok(k, farmer[2])) || (ok(i, master[0]) && ok(j, master[1]) && ok(k, master[2]))) { ans++; vis[i][j][k] = true; } } } } } printf("%d\n", ans); } return 0; }
解题思路(Type 2)
我们可以利用排列组合的公式来计算,首先我们对John的密码进行分析,这个密码能够产生多少种开锁密码。显然,由John的密码推导出的开锁密码的数量与设计者的密码推导出的开锁密码数量是一样多的,证明我就省略了。然后关键的一步就是去重,因为我们之前统计的John密码推导出来的开锁密码可能会与设计者密码推导出来的开锁密码重复,我们需要将重复的去掉,最终就是我们的结果。这种解法的关键在于,我们需要利用乘法原理来计算总共有多少,还有就是去重的计算,最后还有我们对于环形表盘的处理,如果计算距离。这种解法可以将时间复杂度降到最低,优化到
O(1),是一种非常不错的算法。
解题代码(Type 2)
/* ID: yinzong2 PROG: combo LANG: C++11 */ #define MARK #include<cstdio> using namespace std; int n; int farmer[3][5], master[3][5]; int makeNumber(int id, int val, int a[3][5]) { int cnt = 0; int temp; for(int i = -2; i <= 2; i++) { temp = val + i; while(temp <= 0) temp += n; while(temp > n) temp -= n; bool flag = true; for(int j = 0; j < cnt; j++) { if(temp == a[id][j]) { flag = false; break; } } if(flag) { a[id][cnt++] = temp; } } return cnt; } int testNumber(int id, int val, int a[3][5], int len1) { int cnt = 0; int len2 = makeNumber(id, val, a); for(int i = 0; i < len2; i++) { for(int j = 0; j < len1; j++) { if(master[id][i] == farmer[id][j]) { cnt++; break; } } } return cnt; } int main() { #ifdef MARK freopen("combo.in", "r", stdin); freopen("combo.out", "w", stdout); #endif // MARK while(~scanf("%d", &n)) { int x; int cnt1; for(int i = 0; i < 3; i++) { scanf("%d", &x); cnt1 = makeNumber(i, x, farmer); } int sum = cnt1*cnt1*cnt1*2; int cnt2[3]; for(int i = 0; i < 3; i++) { scanf("%d", &x); cnt2[i] = testNumber(i, x, master, cnt1); } sum -= (cnt2[0] * cnt2[1] * cnt2[2]); printf("%d\n", sum); } return 0; }
相关文章推荐
- USACO Section 1.3 Prime Cryptarithm 解题报告
- USACO Section1.3 Mixing Milk 解题报告
- USACO Section 1.3 Wormholes 解题报告
- USACO Section1.3 Wormholes 解题报告
- USACO Section1.4 Mother's Milk 解题报告
- USACO Section 1.5 Prime Palindromes 解题报告
- USACO Section 1.1 Greedy Gift Givers 解题报告
- USACO Section1.2 Milking Cows 解题报告
- USACO Section1.2 Transformations 解题报告
- USACO Section2.2 Subset Sums 解题报告 【icedream61】
- USACO Section2.3 Controlling Companies 解题报告 【icedream61】
- USACO Section1.2 Palindromic Squares 解题报告
- USACO Section 1.1 Friday the Thirteenth 解题报告
- USACO Section1.1 Friday the Thirteenth 解题报告
- USACO Section 2.1 Ordered Fractions 解题报告
- USACO Section1.1 Broken Necklace 解题报告
- USACO Section 2.1 Sorting a Three-Valued Sequence 解题报告
- USACO Section 1.3 Ski Course Design 解题报告
- USACO Section1.3 Barn Repair 解题报告
- USACO Section 1.5 Number Triangles 解题报告