题意:
给出一个最多面板,上面有很多按钮,亮着或没亮,初始是全部没亮;从左上到右下,编号从1开始;,
现在我们给出一个3*3的矩阵,作为按钮规则;
例如
.*.
***
.*.
也就是你按任意建,都把这个建单做是这个3*3矩阵的中间,按照这个图,也就是按一个键,则这个建还有它的上下左右,状态全都转变(如果它已经没有上一行了,则忽略);
给出r,c代表几行几列
然后给出一个固定的3*3的矩阵;表示按钮的规则
问最少按几个全部按钮都亮;
思路:
首先算出每一个按钮的影响有哪几个,用二进制压缩;
影响编号1的,则第一位是1,不影响的按钮位置用0;
然后dfs直接暴力;
注意有一个剪枝可以减少很多时间,就是你已经遍历到第n行,如果第n-2行还有没亮的按钮,则就直接退出,因为一个按钮最多影响到它上一行,上两行没亮的就永远不会亮了;
#include<cstdio>
#include<cstring>
#include<set>
#define ll long long
using namespace std;
const int N = 30;
struct pos {
int x,y;
}p[N];
char g[3][3];
int res[N];
int temp[N];
ll s[N];
bool ok;
int num,r,c,ans;
void init() {
for(int i = 0; i < (r * c); i++) {
int rr = i / c;
int cc = i % c;
for(int j = 0; j < num;j++) {
int x = rr + p[j].x;
int y = cc + p[j].y;
if(x < 0 || y < 0 || x >= r || y >= c)
continue;
ll num = (x * c + y);
s[i] |= (1 << num);
}
}
}
void dfs(int cur,int S,int len) {
if(S == ((1 << (r * c)) - 1) && len < ans) {
ok = 1;
for(int i = 0; i < r * c; i++) {
res[i] = temp[i];
}
return;
}
if((cur / c) >= 2) {
int k = (cur/c) - 2;
for(int i = k * c; i < k * c + c; i++) {
if(!(S & (1 << i)))
return ;
}
}
if(cur == (r * c))
return;
temp[cur] = 1;
dfs(cur + 1, S ^ s[cur],len + 1);
temp[cur] = 0;
dfs(cur + 1, S, len);
}
int main() {
int cas = 1;
while(scanf("%d%d",&r, &c) == 2) {
for(int i = 0 ; i < 3; i++) {
scanf("%s",g[i]);
}
ok = 0;
memset(s, 0, sizeof(s));
memset(temp, 0, sizeof(temp));
num = 0;
ans = 0x3f3f3f3f;
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
if(g[i][j] == '*') {
p[num].x = i - 1;
p[num++].y = j - 1;
}
}
}
init();
printf("Case #%d\n",cas++);
dfs(0,0,0);
bool f = 0;
if(ok) {
for(int i = 0 ; i < r * c; i++) {
if(res[i] == 1) {
if(f)
printf(" ");
if(!f)
f = 1;
printf("%d",i + 1);
}
}
}
if(!ok)
printf("Impossible.");
printf("\n");
}
}