POJ 2965 The Pilots Brothers' refrigerator(状态空间搜索 + 位运算)

题目来源:http://poj.org/problem?id=2965

问题描述

The Pilots Brothers' refrigerator

Time Limit: 1000MS

 

Memory Limit: 65536K

Total Submissions: 29753

 

Accepted: 11537

 

Special Judge

Description

The game “The Pilots Brothers: following the stripy elephant” has a quest where a player needs to open a refrigerator.

There are 16 handles on the refrigerator door. Every handle can be in one of two states: open or closed. The refrigerator is open only when all handles are open. The handles are represented as a matrix 4х4. You can change the state of a handle in any location [i, j] (1 ≤ i, j ≤ 4). However, this also changes states of all handles in row i and all handles in column j.

The task is to determine the minimum number of handle switching necessary to open the refrigerator.

Input

The input contains four lines. Each of the four lines contains four characters describing the initial state of appropriate handles. A symbol “+” means that the handle is in closed state, whereas the symbol “−” means “open”. At least one of the handles is initially closed.

Output

The first line of the input contains N – the minimum number of switching. The rest N lines describe switching sequence. Each of the lines contains a row number and a column number of the matrix separated by one or more spaces. If there are several solutions, you may give any one of them.

Sample Input

-+--
----
----
-+--

Sample Output

6
1 1
1 3
1 4
4 1
4 3
4 4

Source

Northeastern Europe 2004, Western Subregion

------------------------------------------------------------

题意

4×4的手柄矩阵,有“开”和“关”两种状态。每次翻转一个手柄,该手柄同行同列的手柄都会翻转。求将给定手柄矩阵翻转成全开状态的最小翻转次数。

------------------------------------------------------------

思路

把手柄矩阵视作状态空间,求状态空间中的最短路。用广搜可以求解。广搜必备数据结构:广搜队列qvis数组(避免同一个状态重复入队)。

由于需要回溯,所以添加一个回溯数组hop,数组的每个元素存储上一个状态和从上一个状态到这个状态的翻转操作

此题时间卡得很紧,故用0~((1<<16)-1)的整数表示状态,整数的每一个二进制位表示“开”或“关”,翻转操作用状态数与翻转数组异或表示。

------------------------------------------------------------

代码

#include<stdio.h>
#include<queue>

const int STATES = (1<<16);	// 用0~((1<<16)-1)的整数表示状态,整数的每一个二进制位表示“开”或“关”

struct Hop {
	int i, j, pre;		// pre: 之前的状态,i,j: 翻转的坐标
};

struct Node {
	int state, cnt;		// state: 状态,cnt:从初始状态到(i,j)的跳数

	Node(int ss, int cc): state(ss), cnt(cc) {}
	Node(void){}
};

Hop hop[STATES];			// 回溯数组
bool vis[STATES] = {};		// 该状态是否访问过
int xpath[16] = {};			// x坐标回溯路径
int ypath[16] = {};			// y坐标回溯路径
int flip[16] = {0x111f, 0x222f, 0x444f, 0x888f,	// 按位异或翻转数组
				0x11f1, 0x22f2, 0x44f4, 0x88f8,
				0x1f11, 0x2f22, 0x4f44, 0x8f88,
				0xf111, 0xf222, 0xf444, 0xf888
};

int bfs(int pre, int dst)
{
	vis[pre] = 1;
	std::queue<Node> q;
	q.push(Node(pre,0));
	int state = pre, cnt, nstate, i, j;
	while (!q.empty())
	{
		state = q.front().state;
		cnt = q.front().cnt;
		q.pop();
		for (i=0; i<4; i++)
		{
			for (j=0; j<4; j++)
			{
				nstate = state ^ flip[4*i+j];	// 翻转手柄(i,j)
				if (nstate == dst)
				{
					hop[nstate].i = i;
					hop[nstate].j = j;
					hop[nstate].pre = state;
					return cnt+1;
				}
				if (!vis[nstate])
				{
					q.push(Node(nstate, cnt+1));
					vis[nstate] = 1;
					hop[nstate].i = i;
					hop[nstate].j = j;
					hop[nstate].pre = state;
				}
			}
		}
	}
	return -1;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("2965.txt", "r", stdin);
#endif
	char str[5];
	char ch;
	int i,j, state = 0, cnt = 0, ans, cur;
	for (i=0; i<4; i++)
	{
		scanf("%s", str);
		for (j=0; j<4; j++)
		{
			ch = str[j];
			if (ch == '+')				// “关”是1,“开”是0
			{
				state += (1<<(4*i+j));
			}
		}
	}
	ans = bfs(state, 0);
	printf("%d\n", ans);
	cur = 0;
	cnt = ans;
	while (cur != state)	// 回溯
	{
		xpath[--cnt] = hop[cur].i;
		ypath[cnt] = hop[cur].j;
		cur = hop[cur].pre;
	}
	for (i=0; i<ans; i++)
	{
		printf("%d %d\n", xpath[i]+1, ypath[i]+1);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/da_kao_la/article/details/82497537