- 题目大意
有一个4x4的网格,每个网格都有一个开关,开关只有开和关两种状态。改变任一一个开关的状态都回改变其所在的行列每一个开关的状态,要找到一种调整开关的方式使整个网格的开关都是开的状态要求操作开关次数最小,并且将操作的开关输出。
- 解题思路
- 思考阶段一:这道题基本上一看就是poj1753的翻版题目。差别就是操作开关的影响周围开关的方式。1753是影响其上下左右的开关,而这个题目则是影响行列的开关。另外一个差别是这道题需要输出操作的按钮位置。这样的话只需要将1753的代码的flip函数(操作开关函数)改动一下。再将枚举之后的位置使用一个数组存储即可。1753题目解析点击这里
- 思考阶段二:使用1753的方法之后发现超时了,无论是组合法还是位压缩都ac不了,无奈之下只能问百度爸爸。找到了一篇该题的秒解。
- 思考阶段三:已知一个开关的改变会影响其所在行列开关的改变。但是若是将按钮所在行列的开关改变一次会怎样呢?神奇的是除了目标开关会变化之外其行列所在的开关都不会变化(我也不知道为什么那个大神能想到,反正我是想不到)。这样的话假设只有一个开关按钮是关闭状态,那只需要将其自身和行列所有按钮都点击一遍则可以得出最少的解了。但如果有多个按钮是关闭的呢?我们都知道一个按钮重复点击偶次数则按钮会不变。如果使用上述方式求两个关闭开关的最少操作次数,若两个关闭的按钮在同一行或者同一列,则必定会有多余的操作。我们只需要将多余的点击去除就可以得到最少的点击次数了。
- 吐槽
这真的想不到,以后如果有更好的方法再来做。。。。。
- 实现方法
求解过程就是模拟思路三的解题方法。输入数据的时候就记录关闭开关的位置,接着只需要将关闭开关的位置逐一模拟一次思路三的过程(将行列按钮都点击一次),注意开关函数不再是之前的亦或操作,而是将该按钮的操作状态+1,表示其已经操作过了1次。等所有关闭的按钮都模拟一次之后,只需要将按钮状态数组里奇次数的按钮输出即可(偶数次已经抵消了)
- 解题代码
#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;
struct mm
{
int a;
int b;
};
int mlist[4][4]={0};
vector<mm> ml;
vector<mm> resultlist;
int result;
void flip(int a,int b)
{
for(int i=0;i<4;i++)
mlist[a][i]++;
for(int i=0;i<4;i++)
mlist[i][b]++;
mlist[a][b]--;
}
void show()
{
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
cout << mlist[i][j] <<" ";
cout << endl;
}
}
void myinput()
{
for(int i=0; i<4; i++) {
for(int j=0; j<4; j++) {
if(getchar()=='+')
{
mm t;
t.a = i;
t.b = j;
ml.push_back(t);
}
}
getchar();
}
}
void work()
{
for(int i=0;i<ml.size();i++)
{
mm t = ml[i];
flip(t.a,t.b);
}
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
if(mlist[i][j]%2!=0)
{
result++;
mm t;
t.a = i;
t.b = j;
resultlist.push_back(t);
}
}
}
}
int main()
{
myinput();
work();
cout << result<<endl;
for(int i=0;i<resultlist.size();i++)
{
cout << resultlist[i].a+1 <<" "<< resultlist[i].b+1<<endl;
}
return 0;
}