这个题目我以前研究过,但是没有找到过数学方法的解答,只会通过编程来解决。
复杂度为2^m 或 2^n

以5X5的格子为例。
我的思路是这样的.
1.任何一盏灯最多只需要点一次就可以了,而且跟点的顺序无关.
因为一盏灯变不变取决与它周围(包括自己)的灯被点过多少次,
如果是奇数次就变,如果是偶数次就不变.
所以一盏灯如果被点了两次,实际上对它自己以及它周围的灯没有什么贡献.
2.只要第一行的点法确定了,那么后面每一行的点法都确定了.
假设第一行点过之后,第一行还有若干盏灯亮着,
那么第二行只能点那些亮着的灯下面的那些灯,只有这样才不违背第一条原则,
而且能使第一行全灭.
因为假使点了其它的灯,就会使第一行的灯重新变亮,
那么还需要回到第一行去点才能使其变灭,而第一行本来已经完成了,这样就
违背了第一条原则.
跟据以上两条原则,那么只要第一行试遍了00000,00001,00010,....11111
(对于5x5的情况,也就是0--31),1表示点,0表示不点.
按照上面的点法去点,只要看最后一行是不是全灭就行了.
那么所有的解法都可以得到.
程序如下,用的C++
#include <iostream>
#include <vector>
using namespace std;
vector<int> dec2bin(int d)//把十进制数转化成二进制的0,1串
{
vector<int> v;
do{
v.push_back(d%2);
d/=2;
}while(d>0);
return v;
}
int n12int(int n)//把n个1组成的二进制数转化为十进制数,以便确定尝试的上界
{
unsigned int b=0;
for(int i=0;i<n;i++){
b<<=1;
b|=1;
}
return b;
}
class light
{
public:
light(int nn);
~light();
void turn(int i,int j);//点击第i行j列的灯
int test(const vector<int> &v);
//把一个二进制串作为第一行的点法,测试是不是解
void answer();//求出所有的解
void display();//显示各灯状态
void reset();//把所有灯置1
private:
int n;//n*n的灯阵,初始全部为1,要求最后全部变成0
int ** lts;
};
light::light(int nn)
{
n=nn;
lts=new int * [n+2];
//(n+2)*(n+2)的矩阵,相当于在四周各加了一层,这样就不需要单独考虑四条边了
//所要的灯的下标为从1到n
for(int i=0;i<n+2;i++)
lts[i]=new int[n+2];
for(int i=0;i<n+2;i++)
for(int j=0;j<n+2;j++)
lts[i][j]=1;
}
light::~light()
{
for(int i=0;i<n+2;i++)
delete[] lts[i];
delete[] lts;
}
void light::turn(int i,int j)
{
if(i<1||j<1||i>n||j>n){
cout<<"Error"<<endl;
exit(1);
}
lts[i][j]=abs(1-lts[i][j]);
lts[i-1][j]=abs(1-lts[i-1][j]);
lts[i][j-1]=abs(1-lts[i][j-1]);
lts[i][j+1]=abs(1-lts[i][j+1]);
lts[i+1][j]=abs(1-lts[i+1][j]);

//display();
}
void light::display()//显示各灯的状态
{
cout<<"==================="<<endl;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
cout<<lts[i][j];
cout<<endl;
}
cout<<"==================="<<endl;
}
void light::reset()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
lts[i][j]=1;
}
int light::test(const vector<int> &v)
{
if(v.size()>n)exit(0);
reset();
for(int i=0;i<v.size();i++)
if(v[i]==1)turn(1,i+1);
for(int i=2;i<=n;i++)
for(int j=1;j<=n;j++)
if(lts[i-1][j]==1)turn(i,j);
//display();
int tmp;
for(tmp=1;tmp<=n;tmp++)
if(lts[n][tmp]==1)break;//查看最后一行
if(tmp==n+1)return 1;
return 0;
}
void light::answer()
{
int up=n12int(n);//确定尝试的上界
for(int i=0;i<=up;i++){
vector<int> v=dec2bin(i);
if(test(v)){
cout<<n<<'x'<<n<<':';
for(int j=0;j<v.size();j++)
cout<<v[j];
cout<<endl;
}
}
}
main()
{
light l5(5);
l5.answer();
}
输出:
5x5:11
5x5:1011
5x5:01101
5x5:00011
海盗船的键盘,配件方面让我们最熟悉的就是这套可更换的电竞键帽了。几乎所有海盗船的键盘都会给配几个可更换的电竞键帽。但是拔键器也真的是。。。毫无高大上可言。。。
从外包装就能看到,键盘的连接线是可插拔式的TYPE-C接口,这个算是现在键盘的标配了我觉得。这方面真的希望某技好好学学!




造型方面,延续了本系列的特点---采用金属拉丝外壳设计。在键盘的上方设计了多媒体按键,我个人是非常喜欢这种设计的,非常非常方便!
配列方面,现在我们看到的海盗船键盘在地步行的布局都是标准的键帽尺寸,所以大家也不用再像以前那样为了换键帽而头疼了。
这里简单提一嘴,这次K70 TKL采用了二色注塑的PBT透光键帽,字符也是闭口的,审美因人而异我就不多说了。




右上角同样设计了很多方便大家操作的按键,最右边的音量滚轮和静音键相信大家已经不陌生了。稍稍往左一些的三个按键分别是(从左到右):配置文件切换、灯光亮度调整以及WIN锁。这里要说明的是,这款海盗船K70 RGB TKL是有板载设计的。玩家可以把自己习惯的按键模式以及喜欢的灯光统统保存在键盘上,这样即便你更换电脑并且在没有驱动软件的情况下也是可以按自己的习惯使用键盘。



海盗船键盘的拉丝技术已经达到了炉火纯青的底部,工艺上几乎没有可挑剔的地方。但是同时想想,好像海盗船的所有键盘的外形也都差不多,这让我们在使用的时候就稍稍缺乏了一些新鲜感。我是真的希望海盗船能换个键盘的外设设计,至少在视觉上看起来会有些新意。
键盘的左侧有着非常小的字体:K70。



现在为大家解释一下这个“竞赛模式”:在开启“电竞模式”的情况下,键盘默认红色灯光,并且为静态模式,其主要目的是减少我们玩游戏时灯光对玩家的干扰。但是这个红色的灯如果玩家不喜欢的话,可以在软件里调节成你喜欢的灯光,但是依然是静态灯光。这里要注意的是,开启“竞赛模式”的时候,驱动是不识别键盘的,也就是说如果你想调灯光,要在标准模式下让驱动识别,然后调灯光;另外,所有自定义按键和宏,全部禁用。也就是说键盘恢复到标准模式;但是多媒体按键、音量控制等最上面一排的功能按键不受干扰,可以正常使用。在开启“竞赛模式”的时候,键盘后面的那个地方有一个红灯,会保持开启。

如果想让驱动软件识别K70 RGB TKL,必须要升级最新版本的驱动才可以。大家可以从界面上看到,新软件相比老版有非常多的变化,完全改变了之前的操作习惯。但是我觉得新版本的操作体验更好,界面一目了然。左侧还增加了场景灯光模式,可以一键控制所有的海盗船产品灯光,让所有设备联动。





8000Hz的回报率,4000Hz的按键扫描,这个配置目前来说真的非常霸道了!现在市面上大部分键盘的回报率都为1000Hz,而K70 TKL直接提升了8倍!但是这里要提醒各位,因为回报率的提升对CPU的素质有很高的要求,所以不是建议所有人都把回报率设置成8KHz。


