2014-03-01
poj2965,题目类似 poj1753,一开始用 1753 的代码改的,自己跑好多组数据都正确,但是超时了。代码如下:
///2014.3.1
///poj2965
#include <cstdio>
int chess; ///二进制末16位表示锁盘,1表示锁着、0表示打开状态
int step;
bool flag = false; ///标记是否是已经找到解
int solution[16];
///方便最后输出拨动的位置
int forEasyCoutRow[16] = {1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4};
int forEasyCoutCol[16] = {1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4};
void flip(int i) ///拨锁
{
for(int j=0 ; j<16 ; j++){
if( j%4 == i%4 || j/4 == i/4 )
chess = chess ^ 0x1<<j ;
}
}
void dfs(int i,int deep) ///deep表示搜索的深度,也是翻棋子的次数
{
if( step == deep ){
flag = ( chess==0 ); /// 0 分别表示全部的锁都已打开
return;
}
if( i>15 || flag ) return;
flip(i);
solution[deep] = i;
dfs(i+1,deep+1);
if( !flag ){
flip(i);
dfs(i+1,deep);
}
return;
}
void init()
{
chess = 0;
char temp;
for(int i=0 ; i<16 ; i++){
scanf("%c",&temp);
if( temp!='+' && temp!='-' )
scanf("%c",&temp);
if( temp == '+' ){
chess = chess | 0x1 << i ;
}
}
}
int main( )
{
// freopen("in","r",stdin);
// freopen("out","w",stdout);
init();
for(step=0 ; step<=16 ; step++ ){
dfs(0,0);
if( flag )
break;
}
printf("%d\n",step);
for(int i=0 ; i<step ; i++ ){
printf("%d %d\n",forEasyCoutRow[ solution[i] ],forEasyCoutCol[ solution[i] ]);
}
return 0;
}
后来考虑到递归函数较慢,所以改成使用循环直接枚举每一种情况,还是超时,代码如下:
///2014.3.1
///poj2965
#include <cstdio>
int chess; ///二进制末16位表示锁盘,1表示锁着、0表示打开状态
int chess2;
///方便最后输出拨动的位置
int forEasyCoutRow[16] = {1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4};
int forEasyCoutCol[16] = {1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4};
void flip(int i) ///拨锁
{
for(int j=0 ; j<16 ; j++){
if( j%4 == i%4 || j/4 == i/4 )
chess2 = chess2 ^ (0x1<<j) ;
}
}
void init()
{
chess = 0;
char temp;
for(int i=0 ; i<16 ; i++){
scanf("%c",&temp);
if( temp!='+' && temp!='-' )
scanf("%c",&temp);
if( temp == '+' ){
chess = chess | 0x1 << i ;
}
}
}
int main( )
{
// freopen("in","r",stdin);
// freopen("out","w",stdout);
init();
int chess3 =0;
for( ; chess3<=65535 ; chess3++){
chess2 = chess;
for(int i=0 ; i<16 ; i++){
if( chess3 & 0x1<<i )
flip(i);
}
if( chess2==0 )
break;
}
int step=0;
for(int i=0 ; i<16 ; i++){
if( chess3 & 0x1<<i )
step++;
}
printf("%d\n",step);
for(int i=0 ; i<16 ; i++){
if( chess3 & 0x1<<i ){
printf("%d %d\n",forEasyCoutRow[i],forEasyCoutCol[i]);
}
}
return 0;
}
求大神指教该怎么优化。
再然后,看到一种大神的思路:
对于一个“+”,对它所在的行列的每一个位置做题目中的flip,结果是只有该“+”会变成“-”,其余位置没有变化。
所以对初始状态下所有“+”都做一次上述操作,最终的结果是一部分位置对比初始状态做了奇数次翻转,另一部分做了偶数次。偶数次等于没翻转,因此只要打印出奇数次的位置即可。
根据这个思路我的代码如下:
///2014.3.1
///poj2965
#include <cstdio>
int chess; ///二进制末16位表示锁盘,1表示锁着、0表示打开状态
int chess2[16];
///方便最后输出拨动的位置
int forEasyCoutRow[16] = {1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4};
int forEasyCoutCol[16] = {1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4};
void flip(int i) ///拨锁
{
for(int j=0 ; j<16 ; j++){
if( j%4 == i%4 || j/4 == i/4 )
chess = chess ^ (0x1<<j) ;
}
}
void init()
{
chess = 0;
char temp;
for(int i=0 ; i<16 ; i++){
scanf("%c",&temp);
if( temp!='+' && temp!='-' )
scanf("%c",&temp);
if( temp == '+' ){
chess = chess | 0x1 << i ;
}
}
for(int i=0 ; i<16 ; i++){
chess2[16] = 0;
}
}
int main( )
{
// freopen("in","r",stdin);
// freopen("out","w",stdout);
init();
for(int i=0 ; i<16 ; i++){
if( chess & 0x1<<i ){
for(int j=0 ; j<16 ; j++){
if( j%4 == i%4 || j/4 == i/4 ){
flip(j);
chess2[j]++;
}
}
}
}
int step=0;
for(int i=0 ; i<16 ; i++){
if( chess2[i]%2 )
step++;
}
printf("%d\n",step);
for(int i=0 ; i<16 ; i++){
if( chess2[i]%2 ){
printf("%d %d\n",forEasyCoutRow[i],forEasyCoutCol[i]);
}
}
return 0;
}
感触:ACM离不开数学,数学的分析能大大精简算法,或是发现解题的方便方法。