飞行员兄弟

题目:

“飞行员兄弟”这个游戏,需要玩家顺利的打开一个拥有 16个把手的冰箱。已知每个把手可以处于以下两种状态之一:打开或关闭。只有当所有把手都打开时,冰箱才会打开。把手可以表示为一个 4×4的矩阵,您可以改变任何一个位置 [i,j]上把手的状态。但是,这也会使得第 i行和第 j列上的所有把手的状态也随着改变。请你求出打开冰箱所需的切换把手的次数最小值是多少。

输入格式

输入一共包含四行,每行包含四个把手的初始状态。符号 + 表示把手处于闭合状态,而符号 - 表示把手处于打开状态。至少一个手柄的初始状态是关闭的。

输出格式

第一行输出一个整数 N,表示所需的最小切换把手次数。接下来 N行描述切换顺序,每行输出两个整数,代表被切换状态的把手的行号和列号,数字之间用空格隔开。

注意:如果存在多种打开冰箱的方式,则按照优先级整体从上到下,同行从左到右打开。

数据范围

1≤i,j≤4

输入样例:

-+--
----
----
-+--

输出样例:

6
1 1
1 3
1 4
4 1
4 3
4 4

 分析:这题与n-皇后的问题有相似的地方,我的想法是遍历所有的点的所有状态(改变这个点的状态和不改变这个点的状态),直到整个数组符合要求为止。具体的操作用vector<pair<int,int>>来存储。因为没个点都有2种情况,一共有4*4个点,所以时间复杂度为O(2^{4*4}),详细见代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
typedef pair<int, int> PII;

//冰箱的初始把手状态
int map[10][10];

//我们希望的最终的冰箱状态,是为了检验冰箱的把手是否全部打开而存在
int moduel[10][10];

//op表示操作数最短的一组方法,tmp表示当前的这组操作方法
vector<PII> op, tmp;

//改变x行y列的把手状态
void change(int x, int y) {
    for (int i = 1; i <= 4; i++) {
        map[i][y] = 1 - map[i][y];
    }
    for (int j = 1; j <= 4; j++) {
        map[x][j] = 1 - map[x][j];
    }
    map[x][y] = 1 - map[x][y];
}

//检验冰箱把手是否全部打开
bool cmp(int a[10][10], int b[10][10]) {
    for (int i = 1; i <= 4; i++) {
        for (int j = 1; j <= 4; j++) {
            if (a[i][j] != b[i][j])return false;
        }
    }
    return true;
}


void dfs(int x, int y){

    //如果我们将4*4个把手全部遍历完了,就检验是否符合要求
    if(x == 4 && y == 5){
        if(cmp(map, moduel)){
            if(op.empty() || tmp.size() < op.size())
                op = tmp;
        }
        return;
    }

    //边界问题
    if(y == 5)x ++, y = 1;

    //改变状态
    change(x, y);
    tmp.push_back({x, y});
    dfs(x , y + 1);//**
    //还原状态
    change(x , y);
    tmp.pop_back();
    
    //不改变状态
    dfs(x, y + 1);

}
int main(){
    for(int i = 1; i <= 4; i ++){
        for(int j = 1; j <= 4; j++){
            char a;
            cin >> a;
            if(a == '+')map[i][j] = 1;
            else map[i][j] = 0;
        }
    }
    
    //从(1,1)这个点开始遍历
    dfs(1, 1);

    int size = op.size();
    cout << size << endl;
    for(int i = 0; i < size; i ++) cout << op[i].first << " " << op[i].second << endl;
    return 0;
}

扩展:DFS的步骤

        在DFS的过程中,我们先依次从(1,1)开始遍历到(4,4),之后就会dfs(4,5),这个时候进入if(x == 4 && y == 5){....}如果数组没有达到我们的要求,就会return到(4,4)这个点中的**行(在代码中注释了),然后还原状态,走不将其改变状态的这个分支。也就是说,DFS就是不断的试探回溯,如果走的这个分支不行,就回溯回去,将状态还原,再走另外一个分支。如果大家有时间可以去看看n-皇后问题,与这个比较相似.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值