C++飛行員兄弟(列舉)

2020-10-03 11:00:15

「飛行員兄弟」這個遊戲,需要玩家順利的開啟一個擁有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

AC程式碼:

#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>

using namespace std;

const int N=5;//為'\0'多準備一個空間
typedef pair<int,int> PII;//使PII等價於這個型別
#define x first//把所有x替換成first
#define y second

char g[N][N],backup[N][N];//操作盤和備份盤
vector<pair<int,int>> ans;

int get(int x,int y)//計算g[x][y]對應的操作二進位制串位數
{
    return 4*x+y;
}

void turn_one(int x,int y)//翻轉一個開關
{
    if(g[x][y]=='+') g[x][y]='-';
    else g[x][y]='+';
}

void turn_all(int x,int y)//進行一次點選除自身外連帶引起其他開關的翻轉
{
    for(int i=0;i<4;++i)
        turn_one(i,y);
    for(int j=0;j<4;++j)
        turn_one(x,j);
    turn_one(x,y);
}

int main()
{
    for(int i=0;i<4;++i) cin>>g[i];//讀入初始狀態
    memcpy(backup,g,sizeof(g));//備份初始狀態
    for(int op=0;op< 1<<16;++op)//列舉操作序列
    {
        vector<pair<int,int>> temp;//記錄本次操作的序列
        for(int i=0;i<4;++i)//根據列舉方案逐位進行操作
            for(int j=0;j<4;++j)
                if(op>>get(i,j) & 1)
                {
                    temp.push_back(PII{i,j});
                    turn_all(i,j);
                }

        bool flag=true;//判斷本次操作是否成功
        for(int i=0;i<4;++i)
            for(int j=0;j<4;++j)
                if(g[i][j]=='+'){flag=false;break;}
        if(flag){//更新更優答案
            if(temp.size()<ans.size()||ans.empty()) ans=temp;
        }
        memcpy(g,backup,sizeof(g));//還原回初態
    }
    cout<<ans.size()<<endl;//輸出最優解步數
    //因題目中座標從1開始,座標轉換後再輸出
    for(auto it=ans.begin();it<ans.end();++it) cout<<(*it).x+1<<" "<<(*it).y+1<<endl;
    return 0;
}