题目链接。
题意:
给出一个4*4矩阵,每个单元要么是 '+', 要么是 '-',每次可以选一个单元,翻转它以及它所在的行和列。求使全部为'-'的最少操作数,并求出操作步骤。
分析:
这题和 1753 类似,但此题的后台数据显然要多余后者。
可以利用打表,算出所有的翻转状态。存入sw。打表代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
int swc(int i) { int nsw = 0, j; nsw |= (1<<i); j = i; while(j % 4 != 0) { nsw |= (1<<j); j--; } nsw |= (1<<j); j = i; while((j+1) % 4 != 0) { nsw |= (1<<j); j++; } nsw |= (1<<j); j = i; while(j >= 0) { nsw |= (1<<j); j -= 4; } j = i; while(j < 16) { nsw |= (1<<j); j += 4; } return nsw; }
即int sw[16] = {4383, 8751, 17487, 34959, 4593, 8946, 17652, 35064, 7953, 12066,20292, 36744, 61713, 61986, 62532, 63624};
接着直接枚举。 不过要注意的是,如果先入队列,取出时标记,是会TLE的。所以对于新状态要先标记,再放入队列。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <map> #include <queue> using namespace std; const int maxn = (1<<16); int p[maxn], x[maxn], y[maxn], step[maxn], n; bool vis[maxn]; int sw[16] = {4383, 8751, 17487, 34959, 4593, 8946, 17652, 35064, 7953, 12066, 20292, 36744, 61713, 61986, 62532, 63624}; int BFS(int n) { memset(p, 0, sizeof(p)); memset(vis, false, sizeof(vis)); queue<int> Q; Q.push(n); step[n] = 0; vis[n] = true; while(!Q.empty()) { int e = Q.front(); Q.pop(); if(e == 0) return step[e]; for(int i=0; i<16; i++) { int state = e ^ sw[i]; if(!vis[state]) { Q.push(state); vis[state] = true; p[state] = e; x[state] = 4-(i / 4); y[state] = 4-(i % 4); step[state] = step[e] +1; } } } return 0; } void print(int s) { if(s != n) { print(p[s]); printf("%d %d\n", x[s], y[s]); } } int main() { n = 0; char s[10]; for(int i=0; i<4; i++) { cin >> s; for(int j=0; j<4; j++) { n <<= 1; if(s[j] == '+') n |= 1; } } printf("%d\n", BFS(n)); print(0); return 0; }