1. 程式人生 > 實用技巧 >「POJ2965」The Pilots Brothers' refrigerator

「POJ2965」The Pilots Brothers' refrigerator

題目連結 & 題面

AcWing:https://www.acwing.com/problem/content/118/

VirtualJudge:https://vjudge.net/problem/POJ-2965

題目描述

“飛行員兄弟”這個遊戲,需要玩家順利的開啟一個擁有16個把手的冰箱。

已知每個把手可以處於以下兩種狀態之一:開啟或關閉。

只有當所有把手都開啟時,冰箱才會開啟。

把手可以表示為一個4х4的矩陣,您可以改變任何一個位置[i,j]上把手的狀態。

但是,這也會使得第i行和第j列上的所有把手的狀態也隨著改變。

請你求出開啟冰箱所需的切換把手的次數最小值是多少

輸入格式

輸入一共包含四行,每行包含四個把手的初始狀態。

符號“+”表示把手處於閉合狀態,而符號“-”表示把手處於開啟狀態。

至少一個手柄的初始狀態是關閉的。

輸出格式

第一行輸出一個整數N,表示所需的最小切換把手次數。

接下來N行描述切換順序,每行輸入兩個整數,代表被切換狀態的把手的行號和列號,數字之間用空格隔開。

注意:如果存在多種開啟冰箱的方式,則按照優先順序整體從上到下,同行從左到右開啟。

資料範圍

1≤i,j≤4

基本思路

DFS

要想解法最優,首先要保證一個一個把手最多切換1次,如果切換1次以上就是浪費次數。

例如切換2次,和不切換效果相同,切換3次,和切換1次效果相同……不管這n次切換中間經歷了什麼,最後的效果是和0或1次切換效果相同。

這個非常容易證明,只需要證明操作是無序的即可。其實題目輸出的排序,也暗示了這點。

並且,如果不重複切換的話,每種情況是有唯一解的。

這點是我的直覺告訴我的(真的很顯而易見),也就是說,題目中“多種開啟冰箱的方式”也只是開啟的順序不同,解法是相同的。

這樣,對於一個把手,只有切換和不切換兩種狀態,狀態空間大小為 \(2^{16}\) ,也就是65536,完全可以考慮深搜。

二進位制狀態壓縮

其實對於這道題,一共就4×4的範圍,沒什麼必要弄狀態壓縮,但看在書上和 AcWing 給了位運算的tag,就寫一下吧,實際優化的程度並不大。

把當前狀態用16位二進位制表示,我選擇的是從左到右,從上到下(和資料輸出的順序相同)。

每次改變把手的時候就讓相應的位和1做異或運算。

由於我用1表示閉合,用0表示開啟,所以當這個二進位制數等於0的時候,說明找到了解(並且是唯一解),輸出。

程式碼

#include <cstdio>
#include <iostream>

void dfs(int);
int mkbin(int);
int pow(int, int);
char a;
int r, b, x, y, logx[20], logy[20], cnt, ans;

int main(){
	freopen("data.in", "r", stdin);

	for(int i=0; i<16; i++){
		scanf("%c", &a);
		if(a=='\n') i--;
		if(a=='+') { r*=2; r+=1; }
		if(a=='-') r*=2;
	}
	dfs(0);

	return 0;
}

void dfs(int step){
	if(r==0){
		printf("%d\n", cnt);
		for(int i=0; i<cnt; i++){
			printf("%d %d\n", logx[i], logy[i]);
		}
		return;
	}
	if(step==16) return;
	
	dfs(step+1);
	int b = mkbin(step);
	r = r^b;
	logx[cnt]=step/4+1;
	logy[cnt]=step%4+1;
	cnt++;
	dfs(step+1);
	r = r^b;
	cnt--;
}

int mkbin(int num){
	ans=0;
	x=num/4; y=num%4;
	ans^=15*pow(16, 3-x);
	ans^=pow(2, 3-y)*1;
	ans^=pow(2, 3-y)*16;
	ans^=pow(2, 3-y)*256;
	ans^=pow(2, 3-y)*4096;
	ans^=pow(2, 3-y)*pow(16, 3-x);
	return ans;
}

int pow(int c, int d){
	int anss=1;
	for(int i=0; i<d; i++) anss*=c;
	return anss;
}