POJ - 2965 The Pilots Brothers' refrigerator
题意:
4*4的开关图,+表示关,-表示开,只有所有开关状态为 '-' 时,灯才会亮,改变开关(i,j)的状态时,i行的开关和j列的开关状态全部改变。
思路:
dfs+枚举
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#include<stack>
#include<vector>
using namespace std;
typedef long long ll;
int v[100],map[10][10],mi;
int d[100]; //存储最短路径
int judge()
{
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
if(map[i][j]==0)
return 0;
}
}
return 1;
}
void revers(int n) //状态翻转
{
int x = n/4 , y = n%4;
for(int i=0;i<4;i++)
{
map[x][i] ^= 1;
if(i!=x)
map[i][y] ^= 1;
}
}
void dfs(int n,int step)
{
if(judge())
{
if(mi > step)
{
mi = step;
for(int i=1;i<=step;i++)
d[i] = v[i]; //最短路线
}
}
if(n>15)
return ;
dfs(n+1,step); //跳过第n个开关
revers(n); //改变第n个灯的状态
v[step+1] = n; //记录临时路径
dfs(n+1,step+1); //改变第n个开关状态后,继续往下搜
revers(n); //回溯
}
int main()
{
char s;
mi = 30;
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
scanf("%c",&s);
if(s=='+')
map[i][j] = 0; //关
else
map[i][j] = 1; //开
}
getchar();
}
dfs(0,0);
printf("%d\n",mi);
for(int i=1;i<=mi;i++)
printf("%d %d\n",d[i]/4+1,d[i]%4+1);
return 0;
}
还有一种大神的高效解法
如果要把某个+翻成-,把它所在的行和列的所有格都翻一遍就行了。那它本身翻7次,它所在行或列的单位被翻4次,其他位置的被翻2次。翻偶数次和没翻一样,翻7次和翻1次一样。那么定义一个数组,对所有的+做统计,令其所在的行和列的元素都增1,最后再看一下,元素为1的位置就是被翻转过的位置,而元素为奇数的元素个数就是n的值。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#include<stack>
#include<vector>
using namespace std;
typedef long long ll;
int v[10][10];
void mark(int x,int y)
{
for(int i=1;i<5;i++)
{
v[i][y]++;
if(i!=y)
v[x][i]++;
}
}
int main()
{
char s;
int ans = 0;
for(int i=1;i<5;i++)
{
for(int j=1;j<5;j++)
{
scanf("%c",&s);
if(s=='+')
mark(i,j);
}
getchar();
}
for(int i=1;i<5;i++)
{
for(int j=1;j<5;j++)
{
if(v[i][j]%2)
ans++;
}
}
printf("%d\n",ans);
for(int i=1;i<5;i++)
{
for(int j=1;j<5;j++)
{
if(v[i][j]%2)
printf("%d %d\n",i,j);
}
}
return 0;
}
参考博客:
https://blog.csdn.net/hello_sheep/article/details/78507062
https://blog.csdn.net/northsnow_bupt/article/details/11179103