1. 程式人生 > >位運算_列舉_POJ2965_The Pilots Brothers' refrigerator

位運算_列舉_POJ2965_The Pilots Brothers' refrigerator

題目的等價描述:

    給定1個4\times 4的矩陣, 矩陣中每個元素為1(對應關閉)或0(對應開啟), 且至少含有1個1, 定義操作(運算)p(i, j)為將矩陣的第1行,第j列的元素與1進行異或, 同時所有其它的第i行或第j列的元素亦與1進行異或, 求最少經過多少次p操作可將矩陣中的所有元素全部轉換為0, 並輸出轉換次數最少的方案的操作位置序列.

思路分析:

    可以證明, 將每次p操作後的矩陣做為操作的結果, p操作(運算)滿足交換律和結合律, 據此根據近世代數的相關知識可得: 對於任意的p操作序列均存在一個最終結果與之相同且同一位置至多施加p操作一次的p操作序列(注意到同一位置上的偶數次p操作相互抵消), 

故可列舉矩陣每個位置是否施加p操作, 經過2^{16}輪列舉即可求解 ,在具體實現中我們將整個矩陣使用int型變數的低16位儲存, 使用一個int型變數儲存當前最優的p操作序列, 使用位運算完成p運算, 下面給出AC程式碼:

//POJ2965_The Pilots Brothers' refrigerator
#include <iostream>
#include <cstdio>
using namespace std;
const int NIL = 0x3f3f3f3f;
//對矩陣state的第x行, 第y列的元素執行p操作(行, 列編號均從0開始) 
void p(int &state, int x, int y){
	for(int i = 0; i <= 3; ++i) state ^= 1 << 4 * x + i, i != x? state ^= 1 << 4 * i + y: 0;
}
//返回二進位制數n中1的個數 
int lowbit(int n){
	int res = 0;
	while(n) ++res, n -= (n & (-n));
	return res;
}
int main(){
	int state = 0;//初始矩陣
	char ch;
	for(int i = 0; i <= 3; ++i)
		for(int j = 0; j <= 3; ++j) cin >> ch, ch == '+'? state |= 1 << i * 4 + j: 0;  
	int res = (1 << 20) - 1;
	for(int i = 0; i < (1 << 16); ++i){
		if(lowbit(i) >= lowbit(res)) continue;
		int stmp = state; 
		for(int j = 0; j <= 3; ++j)
			for(int k = 0; k <= 3; ++k) if((i >> j * 4 + k) & 1) p(stmp, j, k);
		if(!stmp && lowbit(res) > lowbit(i)) res = i;
	}
	cout << lowbit(res) << endl;
	for(int i = 0; i <= 3; ++i)
		for(int j = 0; j <= 3; ++j) if((res >> i * 4 + j) & 1) cout << i + 1 << " " << j + 1 << endl;		
	return 0;
} 

注意: 第23行的語句是必要的, 該語句使得第24~27行的執行次數不超過16次, 如果掉該語句那麼24~37行將執行2^{16}次, 從而在POJ上超時