1. 程式人生 > >[POJ2965]The Pilots Brothers' refrigerator (搜尋/位運算)

[POJ2965]The Pilots Brothers' refrigerator (搜尋/位運算)

題意

遊戲“The Pilots Brothers:跟隨有條紋的大象”有一個玩家需要開啟冰箱的任務。

冰箱門上有16個把手。每個手柄可以處於以下兩種狀態之一:開啟或關閉。只有當所有把手都開啟時,冰箱才會開啟。手柄表示為矩陣4х4。您可以在任何位置[i,j](1≤i,j≤4)更改控制代碼的狀態。但是,這也會更改第i行中所有控制代碼的狀態以及第j列中的所有控制代碼。

任務是確定開啟冰箱所需的最小手柄切換次數。

思路

一個和“費解的開關”,"棋盤翻轉",這樣的位運算的題目很像,只不過這次一次翻轉1行+1列

其實可以用1個數就可以記錄狀態,但是我懶……

(其實是不會狀壓啦)

Code

#include<cstdio>
using namespace std;
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)

int x[20], y[20];

int map[6][6];
int ans=33;
int ansX[20],ansY[20];

int read()
{
    char ch = getchar();
    while (ch!='-' && ch!='+') ch = getchar();
    return ch=='
-'?1:0; } void build() { for(int i=0;i<4;i++) for (int j = 0; j < 4; j++) map[i][j] = read(); } void flip(int s) { int x1 = s / 4; int y1 = s % 4; for (int i = 0; i < 4; i++) { map[i][y1] ^= 1; map[x1][i] ^= 1; } map[x1][y1]
^= 1; } bool check() { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) if (!map[i][j]) return 0; } return 1; } void dfs(int s, int b) { if (check()) { if (ans > b) { ans = b; for (int i = 1; i <= ans; i++) ansX[i] = x[i], ansY[i] = y[i]; } return; } if (s >= 16) return; dfs(s + 1, b); flip(s); x[b + 1] = s / 4 + 1; y[b + 1] = s % 4 + 1; dfs(s + 1, b + 1); flip(s); return; } int main() { build(); dfs(0, 0); printf("%d\n", ans); for (int i = 1; i <= ans; i++) printf("%d %d\n", ansX[i], ansY[i]); return 0; }

其他做法

部落格上搜到的

放出來

註釋也很詳細了

用奇偶性做的

/*

參考高手的高效解法:
> 證明:要使一個為'+'的符號變為'-',必須其相應的行和列的運算元為奇數;可以證明,如果'+'位置對應的行和列上每一個位置都進行一次操作,則整個圖只有這一'+'位置的符號改變,其餘都不會改變.
> 設定一個4*4的整型陣列,初值為零,用於記錄每個點的運算元,那麼在每個'+'上的行和列的的位置都加1,得到結果模2(因為一個點進行偶數次操作的效果和沒進行操作一樣,這就是樓上說的取反的原理),然後計算整型陣列中一的
> 個數即為運算元,一的位置為要操作的位置(其他原來運算元為偶數的因為操作並不發生效果,因此不進行操作)
*********************************
此上證其可以按以上步驟使陣列中值都為‘-’
********************************
在上述證明中將所有的行和列的位置都加1後,在將其模2之前,對給定的陣列狀態,將所有的位置操作其所存的運算元個次數,舉例,如果a[i][j]==n,則對(i,j)操作n次,當所有的操作完後,即全為‘-’的陣列。
其實就是不模2的操作,作了許多的無用功。
以上的操作次序對結果無影響,如果存在一個最小的步驟,則此步驟一定在以上操作之中。(簡單說下:因為以上操作已經包含了所有可改變欲改變位置的操作了)
而模2後的操作是去掉了所有無用功之後的操作,此操作同樣包含最小步驟。
但模2後的操作去掉任何一個或幾個步驟後,都不可能再得到全為‘-’的。(此同樣可證明:因為操作次序無影響,先進行最小步驟,得到全為‘-’,如果還剩下m步,則在全為‘-’的陣列狀態下進行這m步操作後還得到一個全為
‘-’的陣列狀態,此只能是在同一個位置進行偶數次操作,與前文模2後矛盾,所以m=0),因此模2後的操作即為最小步驟的操作。
*/
#include <iostream>
using namespace std;

bool mark[4][4];
char s[4][4];

int main()
{
    int i,j,k;
    int ci[16],cj[16];
    int nas = 0;
    memset(mark,0,sizeof(mark));
    for(i = 0;i < 4;i++)
        cin >> s[i];
    for(i = 0;i < 4;i++)
        for(j = 0;j < 4;j++)
        {
            char c = s[i][j];
            if(c == '+')
            {
                mark[i][j] = !mark[i][j];
                for(k = 0;k < 4;k++)
                {
                    mark[i][k] = !mark[i][k];
                    mark[k][j] = !mark[k][j];
                }
            }

        }
    for(i = 0;i < 4;i++)
        for(j = 0;j < 4;j++)
            if(mark[i][j] == true)
            {
                ci[nas] = i + 1;
                cj[nas] = j + 1;
                nas ++;
            }
    printf("%d\n",nas);
    for(i = 0;i < nas;i++)
    {
        printf("%d %d\n",ci[i],cj[i]);
    }
    return 0;
}