題目:
1000瓶藥水,其中至多有1瓶劇毒,小狗服完藥20小時後才能判斷是否中毒。
現在給你10只小狗、在24小時內、通過小狗試藥的方式找出哪瓶藥有毒或者全部無毒思路:
一、“小狗服完藥20小時後才能判斷是否中毒”,現只有“24小時內”,那麼只能試一輪。
二、一輪過後,每隻小狗狀態有兩種:生、死
把每隻狗看成二進制數的一位,那麼結果是個10位的二進制數,可表示2^10 即1024種情況
這已經超過了目標總數(1000),因此有可能找到一種編碼方案,將1000種情況唯一的表現出來。
三、將1000瓶藥編號:1~1000,換成2進制:0000000001至1111101000(稱最左邊爲第9位,最右邊爲第0位)
再取10個試管,編號:9876543210
對每瓶藥,查它2進制編號中所有爲1的位,按位序號加到對應試管中。
例如第1000瓶藥的編號爲1111101000,加入第9,8,7,6,5,3號試管。
然後對狗編號,吃下對應試管的藥(混合後的)。
四、20小時後,根據狗的情況:生=0,死=1 得到一個10位2進制數,即毒藥的編號。
以下爲測試程序:
- #include <cassert>
- #include <cstdlib>
- #include <cstring>
- #include <iostream>
- using namespace std;
- class Tester
- {
- bool m_began;
- int m_poison;
- bool m_dogs[10];
- public:
- Tester()
- : m_began(false)
- {
- this->begin();
- this->end();
- }
- void begin(int i = -1)
- {
- m_began = true;
- memset(m_dogs, 0, sizeof(m_dogs));
- m_poison = (i == -1 ? (rand() % 1000) + 1 : i);
- }
- void feed(int dog0to9, int bottle1to1000)
- {
- assert(m_began);
- if(bottle1to1000 == m_poison){
- m_dogs[dog0to9] = true;
- }
- }
- void end()
- {
- m_began = false;
- }
- bool query(int dog0to9)
- {
- assert(!m_began);
- return m_dogs[dog0to9];
- }
- void judge(int poison)
- {
- assert(!m_began);
- if(m_poison != poison){
- cerr << "test failed: estimate " << poison << ", fact " << m_poison << endl;
- exit(0);
- }
- }
- };
- void testCase(Tester & t, int x = -1)
- {
- t.begin(x);
- for(int i = 1; i <= 1000; ++i){ // for each bottle
- for(int x = i, n = 0; x; x >>= 1, ++n){ // each bit
- if(x & 1){
- t.feed(n, i);
- }
- }
- }
- t.end();
- int id = 0;
- for(int i = 9; i >= 0; --i){
- id = (id << 1) | (t.query(i) ? 1 : 0);
- }
- t.judge(id);
- }
- int main()
- {
- Tester t;
- // cover
- for(int x = 0; x < 1001; ++x){
- testCase(t, x);
- }
- // random test
- for(int x = 0; x < 100; ++x){
- testCase(t);
- }
- cout << "passed" << endl;
- return 0;
- }