解猜数字问题
/************************************************************************
文件名: 解猜数字问题 文件描述: 解猜数字问题 创建人: 陈泽丹, 2008年11月3日 版本号: 4.5 ************************************************************************/ /*----------------------------------------------------------------------- 问题描述: 随机给出一个数位上的数字不重复的四位数,计算机每次给你提示这样的格 式的语句:xAyB (0<=x<=4, 0<=y<=4) 其中A表示数字正确位置也正确的,B表示数字正确位置不正确。 例如说: 1A2B (表示1个数字正确位置也正确的,另有两个数字正确位置不正确) 现要求编程尽可能在8次机会内猜出这个四位数。 解题思路: 把它分两步进行,1,先求这个四位数。2,再求这个四位数各位的 位置。这两步又是互相独立的子问题,故可以同时进行解决的. 第一步: 如果猜测的四位数中的某一位就是答案的数字之一,那么只要它出现,提示就会相应 的加1,故可构造一个这样一个表格, 算法如下:其中首纵列为可选数字,首横列为各猜测的序号 (数字,序号) == 1 表示该数字在这一次出现了, (数字,序号) == 0 表示该数字在这一次没有出现。 (例如答案是0,1,2,9,则有表如下 第一次 第二次 第三次 ..... 第八次 0 0 1 0 ..... 1 1: 0 0 0 ..... 1 2: 1 1 1 ..... 1 . . . 9 0 1 0 ..... 0 猜测结果: 1 3 1 ...... 3 其中: 第一次: 0 + 0 + 1 + 0 = 1 第二次: 1 + 0 + 1 + 1 = 3 第三次: 0 + 0 + 1 + 0 = 3 ... 第八次: 1 + 1 + 1 + 0 = 3 也就是说,如果目标数字是“答案数字的话”,那么其累加和会等于每一次的猜测结果。 更进一步优化的话,其实不必每次每格去验算,把每一整行看成是一个十进制数,把组合中 的四个猜测的数一性加在一起看是否为猜测结果那行的“十进制数”即可,如果不是则排除 该组合。 第二步:可以把位置猜测与已经猜过的位置核对一下, 相同位置的放法不用再猜, 相同位置数目多于当时的A提示数目的也不再猜。 ------------------------------------------------------------------------*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 程序使用说明: 第一次先对游戏输入欲猜的那四个数.再把游戏第一次的提示结果(A+B的和)告诉程 序.然后按程序执行操作。 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ #include <iostream> #include <time.h> using namespace std; const int times = 10; //总共可猜10次 const int Len = 4; //选择的数字量 const int Max = 10; //数字总数 const int NUMBER[Max] = {0,1,2,3,4,5,6,7,8,9}; //可选数字 const int Total = 210; //组合情况的总数 const int PositionMax = 24; //每组数字(4个)的排列情况总数 int m_iCount=0; //对任意组合的数量进行统计 int m_iNumTotal[Total][Len]; //记录所有组合情况 int m_iNumber[Max]; //记录数字的编号(以出场情况为编号) static struct GUESS { int A[times]; //记录每次游戏的A指示 int iGuess[times][Len]; //记录每次猜的数字 int iPguess; //iGuess的一维指针 GUESS(){ init(); } void init() { for (int i=0; i<times; i++) { for (int j=0; j<Len; j++) iGuess [j]=-1; A=-1; } iPguess = 0; } void insert(const int Number[Len]) { for(int i=0; i<Len; i++) iGuess[iPguess] = Number; iPguess++; } }m_stGuess; static struct MAYBE { int iMaybe[Total]; //记录可能是答案的组合的编号 int iPmaybe; void init() { for (int i=0; i<Total; i++) iMaybe=-1; iPmaybe=0; } void insert(int i) { iMaybe[iPmaybe] = i; iPmaybe++; } }m_stMayBe; void Init() {//初始化数据 for (int i=0; i<Max; i++) m_iNumber=0; m_iCount=0; m_stGuess.init(); m_stMayBe.init(); } void set(const int Input[Max], int(* Output)[Len], int m=0, int n=0, int set_fist=1) {//0到Max的集合里取Len个数的组合, m为所选择的数字放置的位置, n为选择的数字 static int iTemp[Len]; static int pOutput = 0; if(set_fist == 1) pOutput = 0; int i; if( m == Len) { for (i=0; i<Len; i++) Output[pOutput] = iTemp; m_iCount++; pOutput++; } else if( n < Max) { for( i=0; i<2; i++) { if ( i==0) { iTemp[m] = Input[n]; set(Input, Output, m+1,n+1,0); } else set(Input, Output, m,n+1,0); } } } void order(int Input[Max], int(* Output)[Len], int n = 0, int order_first = 1) {//求m_iPosition数组里的元素所有排列情况 int i, temp; static int pOutput = 0; if(order_first == 1) pOutput = 0; if (n == Len) { for (i=0; i<Len ;i++) Output[pOutput] = Input; pOutput++; } else { for (i=n; i<Len; i++) { temp=Input[n]; Input[n]=Input; Input=temp; order(Input, Output, n+1, 0); temp=Input[n]; Input[n]=Input; Input=temp; } } } void record_number(const int(* Next)[Len], const int line, int OutputNum[Max], GUESS& OutputGuess) {//调整数字编号并记录下已猜过的数字序列,a[line]为下一步预定要猜测的数字序列 int i, temp; for (i=0; i<Max; i++) OutputNum = OutputNum*10; OutputGuess.insert(Next[line]); for (i=0; i<Len; i++) { temp = Next[line]; OutputNum[temp]++; } } int think_1(const int sum, const int(* InputNumTotal)[Len], const int InputNum[Max], MAYBE& OutputMaybe) {//获得符合条件1的所有组合情况, sum为直到当前为止所有猜测的记录 int sumTemp; int numTemp; OutputMaybe.init(); for (int i=0; i<Total; i++) { sumTemp=0; for (int j=0; j<Len; j++) { numTemp = InputNumTotal[j]; sumTemp += InputNum[numTemp]; } if (sumTemp == sum) { OutputMaybe.insert(i); } } if(OutputMaybe.iPmaybe <= 0) return 0; return 1; } int eliminate(const int(* InputNum)[Len], const int n, const GUESS InputGuess, const int pg) {//获得符合条件2的所有组合情况, n记录查找行, pg表示核对之前的猜测 if (pg>=0) { int c=0; for (int i=0; i<Len; i++) if ( InputNum[n] == InputGuess.iGuess[pg]) c++; if (c == 4) return 0; if (c != InputGuess.A[pg]) return 0;//> return eliminate(InputNum, n, InputGuess, pg-1); } else return 1; } int think_2(const int(* InputNum)[Len], const GUESS InputGuess, const MAYBE MayBe, int(* IO)[Len], int& iCHOOSE) {//检验当前假设情况是否满足条件2 int iPosition[Len]; int i=0, choose=0; int k=0; for (int pi=0; pi<MayBe.iPmaybe; pi++) { i = MayBe.iMaybe[pi]; for (int po=0; po<Len; po++) iPosition[po] = InputNum[po]; order(iPosition, IO); for (choose=0; choose<PositionMax; choose++) if ( eliminate(IO, choose, InputGuess, InputGuess.iPguess-1) ) { k=1; break; } if ( k==1 ) break; } if (pi == MayBe.iPmaybe) return 0; iCHOOSE = choose; return 1; } void Head() {//第一次输入取决于运气, 可由用户来决定 int temp[1][Len], limit=0; while( limit < Len) { cout<<"请输入你第一次猜时的第"<<limit+1<<"个数字:"; cin>>temp[0][limit]; for(int i=0; i<limit; i++) if( temp[0] == temp[0][limit]) { cout<<"猜测数字有重复,请重输!"<<endl; break; } if(i >= limit) { if( temp[0][limit] >=0 && temp[0][limit]<Max) limit++; else cout<<"输入数字不在范围内,请重输!"<<endl; } } record_number(temp, 0, m_iNumber, m_stGuess); } void HeadAoto() {//第一次输入取决于运气, 可由用户来决定 int temp[1][Len], limit=0; int i = 0; while( limit < Len) { do { temp[0][limit] = rand()%10; for(i=0; i<limit; i++) if(temp[0] == temp[0][limit]) break; }while(i<limit); limit++; } record_number(temp, 0, m_iNumber, m_stGuess); cout<<"请输入猜测数字: "<<endl; for(i=0; i<Len; i++) cout<<temp[0]<<" "; cout<<endl; } int InputAB(const int i, int& sum) { cout<<"请输入游戏的第"<<i<<"次时A的提示情况:"; cin>>m_stGuess.A[m_stGuess.iPguess-1]; if( m_stGuess.A[m_stGuess.iPguess-1] == 4) { return 1; } int result = 0; cout<<"请输入游戏的第"<<i<<"次时B的提示情况:"; cin>>result; result = result + m_stGuess.A[m_stGuess.iPguess-1]; sum = sum*10 + result; return 0; } int please_input(const int sum) {//提示下一步输入信息,sum为直到当前为止所有猜测的记录 int choose=0; static int NextCase[PositionMax][Len]; //记录每组数字的所有排列情况 if(!think_1(sum, m_iNumTotal, m_iNumber, m_stMayBe)) return 0; if(!think_2(m_iNumTotal, m_stGuess, m_stMayBe, NextCase, choose)) return 0; record_number(NextCase, choose, m_iNumber, m_stGuess); cout<<"请向游戏输入:"; for (int kk=0; kk<Len; kk++) cout<<NextCase[choose][kk]<<" "; cout<<endl; return 1; } int Control() {//控制游戏进程 Init(); set(NUMBER, m_iNumTotal); cout<<"组合情况数目: "<<m_iCount<<endl; //Head(); HeadAoto(); int sum = 0; InputAB(1, sum); for( int t=2; t <= times; t++) { if( !please_input(sum)) { cout<<"该题目无解"<<endl; break; } if(InputAB(t, sum)) break; } return 0; } void main() { srand((unsigned)time(NULL)); char a; do { Control(); cout<<"若输入#退出本程序:"; cin>>a; cout<<endl<<endl; }while (a != '#'); } |