“本世纪初,哈佛大学数学系副教授查理士.理昂纳德.包顿 (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/
920

被折叠的 条评论
为什么被折叠?



