题目大意:输入为4*4矩阵,每个位置代表一个开关,只包含‘+’和‘-’,‘+’代表关闭,‘-’代表打开,矩阵所有位置都为‘-’表示通过,每次打开某一位置开关,它所在行和列开关都会打开,输出通过的最少步骤,并输出步骤;
思路:改变一个开关奇数次效果都是一样的,相当于改变一次开关状态,偶数次相当于没动开关,且改变开关状态的顺序对结果没有影响(这一点自行论证);所以对于一共改变开关状态的次数,共有16种可能,假设某次通过一共改变i个开关状态,则该次数下又有C(n,16)种可能;所以可以采用暴力枚举+DFS求解;
代码如下:
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 6;//只用到了中间4个;
char state[maxn][maxn];//当前矩阵状态
int wayr[maxn],wayc[maxn];//分别记录最短步骤的开关的行和列;
int step,flag;//由于step是从小到大枚举,做成全局变量,flag用来标记是否达到全开状态;
bool judge()//判断是否达到全开状态
{
char c='-';
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
{
if(state[i][j]!=c) return 0;
}
return 1;
}
void change(int x,int y)//用于单一开关改变状态;
{
if(state[x][y]=='+') state[x][y]='-';
else state[x][y]='+';
}
void turn(int x,int y)//用于改变当前开关所在行列开关的状态;
{
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
{
if(i==x||j==y) change(i,j);
}
}
void dfs(int x,int y,int deep)
{
if(deep==step)
{
flag=judge();
return;
}
if(x>4||flag) return;//flag的目的是:当发现最少次数方式并返回结束该次dfs后,又进入其他dfs,这是就可以靠判全局变量flag迅速返回;
wayr[deep]=x;
wayc[deep]=y;//记录开关坐标编号,由于用deep做下表,所以不必担心turn还原后还存留,因为会被新的覆盖掉,注意存放路径的数组下标一般都跟某参数关联,该参数往往与题目密切相关;
turn(x,y);
if(y<4)//如果y==4说明该行已经枚举完毕,让x+1,y=1,进行下一行枚举;总之整个过程就是从(1,1)开始按行枚举;
{
dfs(x,y+1,deep+1);
turn(x,y);//还原以便于下一次dfs;
dfs(x,y+1,deep);
}
else
{
dfs(x+1,1,deep+1);
turn(x,y);
dfs(x+1,1,deep);
}
return;
}
int main()
{
for(int i=1;i<=4;i++)
{
for(int j=1;j<=4;j++)
scanf("%c",&state[i][j]);
getchar();//没有就不对,我不知为啥;
}
flag=0;
for(step=1;step<=16;step++)
{
dfs(1,1,0);//从编号为(1,1)的开关开始枚举;
if(flag) break;
}
printf("%d\n",step);
for(int i=0;i<=step-1;i++)
printf("%d %d\n",wayr[i],wayc[i]);
return 0;
}
样例:
-+-- ---- ---- -+--输出:
6 1 1 1 3 1 4 4 1 4 3 4 4