题目描述
在n(n>=3)枚硬币中有一枚重量不合格的硬币(过轻或者过重),若只有一架天平可以用来称重,且称重的硬币数量没有限制,设计一个算法找出这枚不合格的硬币,使得称重次数最少。
题目分析
本题用的是减治法,我找了很多网上的写法,但是没有符合我想象中的代码,或者就是题目略有不同,所以决定自己写一个。写的时候花了很长时间处理边界问题。
注意本题是n枚硬币,并且没有事先告知硬币是偏重还是偏轻,因此如果就是根据重量的大小来判断,无法得到正确的答案。
递归C++
主要的思想是根据数组的奇偶来分:
- 长度为奇数时,先不考虑第一个元素,对往后的元素分两段求和,判断它们的大小。如果不相等,则先后递归处理左右两段。如果相等,并且第一个元素不等于第二个元素(这个条件很重要,若只是后面两段相等,并不能得出第一个元素就是假币),那么第一个元素就是假币
- 长度为偶数时,将数组分成长度相等的两段,并分别求和。若相等,则假币不在这个区间里;若不等,递归处理左右两段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| #include <iostream> #include <vector> #include <ctime> #include <algorithm> using namespace std; vector<int> coins; int N;
void make_coins() { int tweight = rand() % 10 + 1; for (int i = 0; i < N; i++) coins[i] = tweight; int fweight = rand() % 20 + 1; int pos = rand() % N; while (fweight == tweight) fweight = rand() % 20 + 1; coins[pos] = fweight; }
int sum_arr(int l, int r) { int sum = 0; for (int i = l; i <= r; i++) sum += coins[i]; return sum; }
int dfs(int n, int l, int r) { if(n == 2) { if (coins[l] != coins[r]) { if ((l-1) >= 0) return coins[l-1] == coins[l]? (r+1):(l+1); else return coins[r+1] == coins[l]? (r+1):(l+1); } return -1; } if (n == 1) { if ((l-1) >= 0 && coins[l-1] != coins[l] && coins[l] != coins[l+1]) return l+1; if ((l-1) < 0 && coins[r+1] != coins[r] && coins[r+2] != coins[r]) return r+1; return -1; } int pos = -1; if (n % 2 == 0) { int rr = l+(r-l)/2; int left = sum_arr(l, rr); int right = sum_arr(rr+1, r); if (left != right) { pos = dfs(n/2, l, rr); if (pos != -1) return pos; pos = dfs(n/2, rr+1, r); if (pos != -1) return pos; } } else { int rr = l+(r-l)/2; int left = sum_arr(l+1, rr); int right = sum_arr(rr+1, r); if (left == right ) { if (coins[l] != coins[l+1]) pos = l + 1; } else { pos = dfs(n/2, l+1, rr); if (pos != -1) return pos; pos = dfs(n/2, rr+1, r); if (pos != -1) return pos; } } return pos; }
void PrintOut() { for (auto t: coins) cout << t << ' '; cout << endl; }
bool check_ans(int n) { int val = coins[n]; int idx; for (int i = 0; i < coins.size(); i++) if (val == coins[i]) { idx = i; break; } if (idx == n) return true; else return false; }
int main() { srand((unsigned int)time(NULL)); N = rand() % 30 + 3; coins.resize(N); make_coins(); PrintOut(); int pos = dfs(N, 0, N-1); printf("共有%d枚硬币\n", N); printf("假币重量为%d,位于第%d个位置\n", coins[pos-1], pos); bool flag = check_ans(pos-1); if (flag) printf("经遍历验证,答案正确!\n"); else printf("答案错误!\n"); return 0; }
|