蓝桥杯学习记录||116. 飞行员兄弟

AcWing||116. 飞行员兄弟

活动地址:https://www.acwing.com/activity/content/19/

考察要点:枚举 位运算

题目要求

“飞行员兄弟”这个游戏,需要玩家顺利的打开一个拥有 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

题目地址:https://www.acwing.com/problem/content/118/

解析 :题目要求为把所有把手打开,但是操作把手会将把手的那一行和那一列的状态都改变(“十”字变化),进行暴力枚举所有的情况

如图若操作,红色框的把手,则蓝色框内的所有把手都会变化一次且仅一次(红色框也是变一次)
蓝桥杯学习记录||116. 飞行员兄弟_第1张图片

思路 :对 将输入的“-”“+”转为 1,0。把手打开为1

对第一个把手有开与不开 两种情况,都进行枚举,对第二个把手也是,以此类推。对所有把手都枚举完成时,判断是否所有开关都打开了,则将把手操作记录起来,后续有更少的操作则更新。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
 using namespace std;
char a[4][4];                 //记录把手状态

typedef pair<int, int> PII;
vector<PII> ans, temp;               //记录操作的把手位置

void turn(int x, int y)
{
    for (int i = 0; i < 4; i ++)
    {
        a[x][i] ^= 1;   //改变该行
        a[i][y] ^= 1;  //改变该列
    }
    a[x][y] ^= 1;
}


void dfs(int x, int y)
{
    //如果所有把手都试了
    if(x == 3 && y == 4)  
    {
        //下面查看所有把手是否都打开了
        for (int i = 0; i < 4; i ++ )
            for (int j = 0; j < 4; j ++ )
            {
                if(a[i][j] == 0) //如果有把手关闭,则不成功
                {
                    return;
                }
            }
        if (ans.empty() || temp.size() < ans.size())   //如果答案步骤为空,
             ans = temp;                               //或者有更小的操作步骤则更新答案
        return;
    }
    //判断是否需要换行
    if(y == 4) 
    {
       x ++;
       y = 0;
    }
    
    
    char backup[4][4];  //备份把手
    memcpy(backup, a, sizeof a);  //备份把手情况
    
    //操作把手,记录把手位置,枚举下一个把手
     turn(x, y);      
     temp.push_back({x,y});
     dfs(x, y + 1);
    
    //不操作该处把手,先清除该把手位置,再还原把手情况
     temp.pop_back();
     memcpy(a, backup, sizeof a);
     dfs(x, y + 1);
    
}

int main()
{
    char ch;
    for (int i = 0; i < 4; i ++)
    {
        for (int j = 0; j < 4; j ++)
        {
            ch = getchar();
            if (ch == '-')      //把手打开为 1 关闭为 0
                a[i][j]=1;
        }
        getchar();
    }
    dfs(0,0);
    
    cout << ans.size() <<endl;
    for (int i = 0; i < ans.size(); i ++ )
        printf("%d %d\n",ans[i].first + 1, ans[i].second + 1);  //答案的数据是从 1 开始的需要加一
    return 0;
}

题目链接:116. 飞行员兄弟
相关题目:95. 费解的开关

你可能感兴趣的:(ACwing,蓝桥杯,算法,c++)