“本世纪初,哈佛大学数学系副教授查理士.理昂纳德.包顿 (Chales Leonard Bouton) 提出一篇极详尽的分析和证明,利用数的二进位表示法,解答了这个游戏的一般法则:对任意列数的铜板,每列有任意枚数,如何取得致胜之道? 在包顿的术语中,拿过后剩下的残局不是安全 (safe) 就是不安全 (unsafe) 的局面。在所有安全的情况下,不管对方如何拿总是到一不安全的情况,你可以再取适当枚数的铜板(在适当的某一列),达到另一安全的情况,这样一直到拿光铜板为止,当然最后一次拿光铜板的一定是你。反之,你如果留下不安全的情况,对方必有方法在适当的某一列,取走适当枚数的铜板,达到他的安全情况,也就是说你输定了。 包顿的方法很简单。首先,将各列铜板的枚数化成二进位数,相加,但不进位,然后再看和的各个位数。如果和的各个位数都是偶数,则表示一安全残局;否则,如果有一位是奇数,则为不安全残局。例如「三、四、五」游戏,一开始就是不安全残局,先拿的人可以适当取二枚而造成他的安全残局。 “ 为什麼安全残局和不安全残局可以利用上述的方法判定呢?这个道理其实很简单。首先,如果将各列铜板数化为二进位表示法,相加,但不进位,得到的各个位数都是偶数的话,不论对方取那一列,多少枚铜板,则那一列铜板数所对应的二进位表示法中,必有某一位或数位由 0 变成 1 或者由 1 变成 0,其相加的和也相对的有某一位或数位由偶数变成奇数 相反的,如果和的某一位或数位是奇数,则我们有办法在某一列取走适当枚数的铜板,使得新的和的各个位数都是偶数。首先,选取和中所有为奇数的各个位数;其次看这些位数中那一个是最左边一位;找某一列,使其二进位表示法在此位上刚好是 1;然后将此列的铜板数所对应的二进位数中,凡是第 ai 位,都改变其数值,亦即若为 0 则变为 1,若为 1 则变为 0,如此得到一新数,我们只要在此列铜板取走适当的数目,使达到这新的枚数,即可以使新的和的各个位数都是偶数;
(摘抄《百度知道》)
根据以上思路,我写了一个关于给定一个数列,判断其是否为安全残局,如果不是,改变一个数使其成为安全残局的程序。源码如下:
#include #include #include using namespace std; #define BIT_SIZE 8 // // 判定給定的數列是否為安全數列 // 如果它們的異或值為0,則為安全數列,否則為不安全數列 // bool IsNumsSafe(const vector &nums) { int result = nums[0]; if (nums.size() > 1) { for (int i = 1; i < nums.size(); ++i) { result ^= nums[i]; } } if (result == 0) { return true; } return false; } // // 顯示數列 // void ShowNums(const vector &nums) { for (int i = 0; i < nums.size(); ++i) { cout << nums[i] << " "; } cout << endl; } // // 將一個整數數列轉換為二進制表示的數列 // vector< bitset > ChangeNumsToBitnums(const vector &nums) { vector< bitset > numBits; for (int i = 0; i < nums.size(); ++i) { bitset bNum(nums[i]); numBits.push_back(bNum); } return numBits; } // // 計算所有二進制數列中每個位上的數字之和 // 如1010、0110、0010的每個位上的和分別為1、1、3、0 // void CaculateBitnumsBitSum(const vector< bitset > &numBits, int *bitSum) { for (int i = 0; i < BIT_SIZE; ++i) { bitSum[i] = 0; for (int j = 0; j < numBits.size(); ++j) { bitSum[i] += numBits[j][i]; } } } // // 找到數列中需要改變的數字 // int FindNumToChange(const int* bitSum, const vector< bitset > &numBits) { // 從高位往低位找,找到第一個奇數對應的位 int i = 0; int ddPos = -1; for (i = BIT_SIZE - 1; i >= 0; --i) { if (bitSum[i] % 2 != 0) { ddPos = i; break; } } // 找到奇數位對應為數字1的那個數,這個數就是需要改變的數 int selectIndex = -1; for (i = 0; i < numBits.size(); ++i) { if (numBits[i].test(oddPos)) { selectIndex = i; break; } } return selectIndex; } // // 將一系列給定的數字轉換為安全數列 // 如果數列已經是安全數列,則不做變更 // 否則,減小其中一個數字,使數列成為安全數列 // bool ChangeNumbersToSafe(vector &nums) { if (nums.size() < 1) { cout << "給定的數列錯誤!至少要給一個數!" << endl; return false; } // 先判斷給定的數字是否已經為安全數組 if (IsNumsSafe(nums)) { cout << "給定的數列已經是安全的,無須更改任何數字!" << endl; return false; } // 將數列轉換為二進制形式 vector< bitset > numBits; numBits = ChangeNumsToBitnums(nums); int bitSum[BIT_SIZE]; CaculateBitnumsBitSum(numBits, bitSum); int selectIndex = -1; selectIndex = FindNumToChange(bitSum, numBits); // 計算除了需要改變的數字外所有的二進制數上每個位上的數字和 int i = 0; for (i = 0; i < BIT_SIZE; ++i) { bitSum[i] -= numBits[selectIndex][i]; } // 找出可以使每個位上的和為偶數的數字 bitset myBitNum; for (i = BIT_SIZE - 1; i >= 0; --i) { if (bitSum[i] % 2 != 0) { myBitNum.set(i); } } // 將二進制數轉換為整型數字 nums[selectIndex] = myBitNum.to_ulong(); return true; } // // 主函數 // int main() { vector nums; nums.push_back(1); nums.push_back(3); nums.push_back(4); nums.push_back(5); ShowNums(nums);
ChangeNumbersToSafe(nums); ShowNums(nums); return 0; }
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/24494482/viewspace-672211/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/24494482/viewspace-672211/