数字小游戏

数字小游戏

最近三蹦子出了一个数字小游戏,游戏规则如图。玩了几局之后,突然兴起想写个程序来求解。没深究过算法,用的是最笨的枚举法。

游戏规则

如图在这里插入图片描述

思路分析

按照我自己的手推过程,先找到3或者4填入,然后再找单个数字的行或列填空。但是按照计算机的处理逻辑,我们需要逐行(列)填入并逐列(行)分析,这里以列填入为例。整体思路就是回溯+剪枝,从第一列开始遍历该列每一种可行的填法,依次遍历所有列。每填一列都要对改变的行进行检查,看看是否合法。若都合法则填下一列,否则剪枝。只要最后结果的行数字与题目给定的相同,则结果正确。

算法解析

首先是数据结构。我们用4×4的二维数组来存储整个方框,0和1分别表示未填和已填。对于行列数字,由于数量不固定(1个或2个数字),这里我们采用vector容器来存储(当然也可以用长度为2的二维数组)。row存储行数字,col存储列数字。

int graph[4][4]={0};
vector<int>row[4],col[4];

接下来是列数字的填法,如下:
数字:1 填法:0,1,2,3
数字:2 填法:01,12,23
数字:3 填法:012,123
数字:4 填法:0123
数字:11 填法:02,13,03
数字:12 填法:013,023
填法的种类是很有限的,所有我们直接枚举就行。
这里将一个和两个数字分开进行枚举填空,check(i)表示检查此时第i行是否合法,合法则返回1。

	if(col[column].size()==1){  //一个数
        //数字是1;
        if(col[column][0]==1){
            for(int i=0;i<4;i++){
                graph[i][column]=1;
                if(check(i)){
                    seek(column+1);
                }
                graph[i][column]=0;	//恢复现场
            }
        }
        //数字是2
        if(col[column][0]==2){
            graph[0][column]=graph[1][column]=1;    //填第0和1行
            if(check(0)&&check(1)){
                seek(column+1);
            }
            graph[0][column]=0;						//恢复现场
            graph[2][column]=1;                     //填第1和2行
            if(check(1)&&check(2)){
                seek(column+1);
            }
            graph[1][column]=0;						//恢复现场
            graph[3][column]=1;                     //填第2和3行
            if(check(2)&&check(3)){
                seek(column+1);
            }
            graph[2][column]=graph[3][column]=0;	//恢复现场
        }
        //数字是3
        if(col[column][0]==3){
			graph[1][column]=graph[2][column]=1;  	
            graph[0][column]=1;						//填第0,1和2行
            if(check(0)&&check(1)&&check(2)){
                seek(column+1);
            }
            graph[0][column]=0;						//恢复现场
            graph[3][column]=1;                     //填第1,2和3行
            if(check(1)&&check(2)&&check(3)){
                seek(column+1);
            }
            graph[1][column]=graph[2][column]=graph[3][column]=0;//恢复现场
        }
        //数字是4
        if(col[column][0]==4){
            graph[0][column]=graph[1][column]=graph[2][column]=graph[3][column]=1;    
            //填每一行,这里不需要判断可以直接填下一列。
            seek(column+1);
            graph[1][column]=graph[2][column]=graph[3][column]=0;
        }
    }
    else{   //有两个数
        //数字是两个1
        if(col[column][0]==col[column][1]){
            graph[0][column]=graph[2][column]=1;    //填第0和2行
            if(check(0)&&check(2)){
                seek(column+1);
            }
            graph[0][column]=graph[2][column]=0;
            graph[1][column]=graph[3][column]=1;    //填第1和3行
            if(check(1)&&check(3)){
                seek(column+1);
            }
            graph[1][column]=graph[3][column]=0;
            graph[0][column]=graph[3][column]=1;    //填第0和3行
            if(check(0)&&check(3)){
                seek(column+1);
            }
            graph[0][column]=graph[3][column]=0;
        }
        //数字是1和2
        else{
            graph[0][column]=graph[3][column]=1;
            graph[2][column]=1;                     //填第0,2和3行
            if(check(0)&&check(2)&&check(3)){
                seek(column+1);
            }
            graph[2][column]=0;
            graph[1][column]=1;                     //填第0,1和3行
            if(check(0)&&check(1)&&check(3)){
                seek(column+1);
            }
            graph[1][column]=0;
            graph[0][column]=graph[3][column]=0;
        }
    }

最后是检查函数check。对于当前行我们先将已有的行数字提取出来,具体操作类似于提取句子中的单词。我们用容器p来存储某一行的行数字,如下:

	vector<int>p;
    int n=0;
    for(int i=0;i<4;i++){
        if(graph[r][i])
            n++;
        else if(n>0){
            p.push_back(n);
            n=0;
        }
    }
    if(n>0){
        p.push_back(n);
    }

然后进行比较。如果第r行是合法的,那么p中数字的个数一定小于或等于row[r](举个反例,p里面是1和2,但是row[r]中只有2,那么往后再怎么填p和row[r]也不可能相同。),小于的话往后的列继续填空有机会使p和row[r]相同。具体算法如下(这里return 1表示合法,0表示非法。):

if(p.size()==row[r].size()){
	if(p.size()==1&&p[0]<=row[r][0])
		return 1;
	if((p[0]<=row[r][0]&&p[1]<=row[r][1])||(p[0]<=row[r][1]&&p[1]<=row[r][0]))
    	return 1;
}
if(p.size()<row[r].size()&&(p[0]<=row[r][0]||p[0]<=row[r][1]))
	return 1;

部分读者可能会有疑问,这里的比较用的都是小于等于,最后的结果一定是等于的吗?答案是肯定的。容易得到,行数字的和等于列数字的和。我们将行数字填入图中,它们的总和是不变的。如果图中每一行的行数字都小于等于题给的行数字,那么最后的结果只可能是都相等。

完成代码

先理一遍整体过程:
1、输入共8行,每行第一个数字n代表个数,后面跟着n个数字,先列数字后行数字;
2、将行列数字分别存入row和col容器中;
3、seek(int column)函数:若到达第四列,说明得到结果,可直接打印;否则继续走流程;
4、check(int r)函数:判断当前该行是否合法,是返回1,否返回0。
代码中的注释为调试使用,读者可自行删除。

#include <iostream>
#include <vector>
using namespace std;
int graph[4][4]={0};
vector<int>row[4],col[4];
int cnt,pos;
void print(){
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++)
            cout<<graph[i][j]<<' ';
        cout<<endl;
    }
}
int check(int r){
    //cout<<"row "<<r<<" :";
    vector<int>p;
    int n=0;
    for(int i=0;i<4;i++){
        if(graph[r][i])
            n++;
        else if(n>0){
            p.push_back(n);
            //cout<<n<<endl;
            n=0;
        }
    }
    if(n>0){
        //cout<<n<<endl;
        p.push_back(n);
    }
    if(p.size()==row[r].size()){
        if(p.size()==1&&p[0]<=row[r][0])
            return 1;
        if((p[0]<=row[r][0]&&p[1]<=row[r][1])||(p[0]<=row[r][1]&&p[1]<=row[r][0]))
            return 1;
    }
    if(p.size()<row[r].size()&&(p[0]<=row[r][0]||p[0]<=row[r][1]))
        return 1;
    return 0;
}
void seek(int column){  //fill the blank
    if(column>=4){
        cout<<"the answer is :"<<endl;
        print();
        return;
    }
    //cout<<"column "<<column<<" has "<<col[column].size()<<endl;
    if(col[column].size()==1){  //just one number;
        //the number is 1;
        if(col[column][0]==1){
            for(int i=0;i<4;i++){
                graph[i][column]=1;
                if(check(i)){
                    //cout<<"one YES in number "<<i<<endl;
                    //print();
                    seek(column+1);
                }
                graph[i][column]=0;
            }
        }

        //the number is 2;
        if(col[column][0]==2){
            graph[0][column]=graph[1][column]=1;    //row 0 and 1
            if(check(0)&&check(1)){
                //cout<<"the first two"<<endl;
                //print();
                seek(column+1);
            }
            graph[0][column]=0;
            graph[2][column]=1;                     //row 1 and 2
            if(check(1)&&check(2)){
                //cout<<"the second two"<<endl;
                //print();
                seek(column+1);
            }
            graph[1][column]=0;
            graph[3][column]=1;                     //row 2 and 3
            if(check(2)&&check(3)){
                //cout<<"the three two"<<endl;
                //print();
                seek(column+1);
            }
            graph[2][column]=graph[3][column]=0;
        }

        //the number is 3;
        if(col[column][0]==3){
            graph[0][column]=graph[1][column]=graph[2][column]=1;    //row 0, 1 and 2
            if(check(0)&&check(1)&&check(2)){
                //cout<<"the first three"<<endl;
                //print();
                seek(column+1);
            }
            graph[0][column]=0;
            graph[3][column]=1;                     //row 1, 2 and 3
            //cout<<endl<<"the second three"<<endl<<"check(1): "<<endl<<check(1)<<endl<<"check(2): "<<endl<<check(2)<<endl<<"check(3): "<<endl<<check(3)<<endl;
            if(check(1)&&check(2)&&check(3)){
                //cout<<"the second three"<<endl;
                //print();
                seek(column+1);
            }
            graph[1][column]=graph[2][column]=graph[3][column]=0;
        }
        if(col[column][0]==4){
            graph[0][column]=graph[1][column]=graph[2][column]=graph[3][column]=1;    //all rows
            //cout<<"all four"<<endl;
            //print();
            seek(column+1);
            graph[1][column]=graph[2][column]=graph[3][column]=0;
        }
    }

    else{   //two numbers
        //1 and 1
        if(col[column][0]==col[column][1]){
            graph[0][column]=graph[2][column]=1;    //row 0 and 2
            if(check(0)&&check(2)){
                //cout<<"one and three"<<endl;
                //print();
                seek(column+1);
            }
            graph[0][column]=graph[2][column]=0;
            graph[1][column]=graph[3][column]=1;    //row 1 and 3
            if(check(1)&&check(3)){
                //cout<<"two and four"<<endl;
                //print();
                seek(column+1);
            }
            graph[1][column]=graph[3][column]=0;
            graph[0][column]=graph[3][column]=1;    //row 0 and 3
            if(check(0)&&check(3)){
                //cout<<"two and four"<<endl;
                //print();
                seek(column+1);
            }
            graph[0][column]=graph[3][column]=0;
        }
        //1 and 2
        else{
            graph[0][column]=graph[3][column]=1;
            graph[2][column]=1;                     //row 0, 2 and 3
            if(check(0)&&check(2)&&check(3)){
                //cout<<"one, three and four"<<endl;
                //print();
                seek(column+1);
            }
            graph[2][column]=0;
            graph[1][column]=1;                     //row 0, 1 and 3
            if(check(0)&&check(1)&&check(3)){
                //cout<<"one, two and four"<<endl;
                //print();
                seek(column+1);
            }
            graph[1][column]=0;
            graph[0][column]=graph[3][column]=0;
        }
    }
}
int main(){
    /*
    col[0].push_back(3);
    col[1].push_back(1);
    col[2].push_back(3);
    col[3].push_back(1);
    col[3].push_back(1);

    row[0].push_back(2);
    row[0].push_back(1);
    row[1].push_back(1);
    row[1].push_back(1);
    row[2].push_back(1);
    row[2].push_back(1);
    row[3].push_back(2);
    */
    for(int i=0;i<4;i++){
        cin>>cnt;
        for(int j=0;j<cnt;j++){
            cin>>pos;
            col[i].push_back(pos);
        }
    }
    for(int i=0;i<4;i++){
        cin>>cnt;
        for(int j=0;j<cnt;j++){
            cin>>pos;
            row[i].push_back(pos);
        }
    }
    seek(0);
    return 0;
}

以下是对图片中的例子进行求解的结果:
在这里插入图片描述

写在后面

已经很久没写过程序了,有点手生。本来也只是觉得这个游戏很适合用做编程题。当然不会有读者拿这个去过游戏吧,毕竟输入数据的时间,手推都得到结果了哈哈哈!如果读者有其他不同的解法,欢迎评论区谈论。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泠然鸢鸣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值