poj2965 递归枚举,直接枚举都超时了,最后求助于Discuss中的方法

2014-03-01

  poj2965,题目类似 poj1753,一开始用 1753 的代码改的,自己跑好多组数据都正确,但是超时了。代码如下:

 1 ///2014.3.1
 2 ///poj2965
 3 
 4 #include <cstdio>
 5 
 6 int chess;         ///二进制末16位表示锁盘,1表示锁着、0表示打开状态
 7 int step;
 8 bool flag = false; ///标记是否是已经找到解
 9 int solution[16];
10 ///方便最后输出拨动的位置
11 int forEasyCoutRow[16] = {1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4};
12 int forEasyCoutCol[16] = {1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4};
13 
14 void flip(int i)  ///拨锁
15 {
16     for(int j=0 ; j<16 ; j++){
17         if( j%4 == i%4  ||  j/4 == i/4 )
18             chess = chess ^ 0x1<<j ;
19     }
20 }
21 
22 void dfs(int i,int deep)   ///deep表示搜索的深度,也是翻棋子的次数
23 {
24     if( step == deep ){
25         flag = ( chess==0 ); /// 0 分别表示全部的锁都已打开
26         return;
27     }
28     if( i>15 || flag ) return;
29     flip(i);
30     solution[deep] = i;
31     dfs(i+1,deep+1);
32     if( !flag ){
33         flip(i);
34         dfs(i+1,deep);
35     }
36     return;
37 }
38 
39 void init()
40 {
41     chess = 0;
42     char temp;
43     for(int i=0 ; i<16 ; i++){
44         scanf("%c",&temp);
45         if( temp!='+' && temp!='-' )
46             scanf("%c",&temp);
47         if( temp == '+' ){
48             chess = chess | 0x1 << i ;
49         }
50     }
51 }
52 
53 int main( )
54 {
55 //    freopen("in","r",stdin);
56 //    freopen("out","w",stdout);
57 
58     init();
59 
60     for(step=0 ; step<=16 ; step++ ){
61         dfs(0,0);
62         if( flag )
63             break;
64     }
65 
66     printf("%d\n",step);
67     for(int i=0 ; i<step ; i++ ){
68         printf("%d %d\n",forEasyCoutRow[ solution[i] ],forEasyCoutCol[ solution[i] ]);
69     }
70     return 0;
71 }
View Code

 

  后来考虑到递归函数较慢,所以改成使用循环直接枚举每一种情况,还是超时,代码如下:

 1 ///2014.3.1
 2 ///poj2965
 3 
 4 #include <cstdio>
 5 
 6 int chess;         ///二进制末16位表示锁盘,1表示锁着、0表示打开状态
 7 int chess2;
 8 ///方便最后输出拨动的位置
 9 int forEasyCoutRow[16] = {1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4};
10 int forEasyCoutCol[16] = {1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4};
11 
12 void flip(int i)  ///拨锁
13 {
14     for(int j=0 ; j<16 ; j++){
15         if( j%4 == i%4  ||  j/4 == i/4 )
16             chess2 = chess2 ^ (0x1<<j) ;
17     }
18 }
19 
20 void init()
21 {
22     chess = 0;
23     char temp;
24     for(int i=0 ; i<16 ; i++){
25         scanf("%c",&temp);
26         if( temp!='+' && temp!='-' )
27             scanf("%c",&temp);
28         if( temp == '+' ){
29             chess = chess | 0x1 << i ;
30         }
31     }
32 }
33 
34 int main( )
35 {
36 //    freopen("in","r",stdin);
37 //    freopen("out","w",stdout);
38 
39     init();
40     int chess3 =0;
41     for( ; chess3<=65535 ; chess3++){
42         chess2 = chess;
43         for(int i=0 ; i<16 ; i++){
44             if( chess3 & 0x1<<i )
45                 flip(i);
46         }
47         if( chess2==0 )
48             break;
49     }
50 
51     int step=0;
52     for(int i=0 ; i<16 ; i++){
53         if( chess3 & 0x1<<i )
54             step++;
55     }
56     printf("%d\n",step);
57     for(int i=0 ; i<16 ; i++){
58         if( chess3 & 0x1<<i ){
59             printf("%d %d\n",forEasyCoutRow[i],forEasyCoutCol[i]);
60         }
61     }
62     return 0;
63 }
poj2965 直接枚举

 

  求大神指教该怎么优化。

再然后,看到一种大神的思路:

  对于一个“+”,对它所在的行列的每一个位置做题目中的flip,结果是只有该“+”会变成“-”,其余位置没有变化。

  所以对初始状态下所有“+”都做一次上述操作,最终的结果是一部分位置对比初始状态做了奇数次翻转,另一部分做了偶数次。偶数次等于没翻转,因此只要打印出奇数次的位置即可。

根据这个思路我的代码如下:

 1 ///2014.3.1
 2 ///poj2965
 3 
 4 #include <cstdio>
 5 
 6 int chess;         ///二进制末16位表示锁盘,1表示锁着、0表示打开状态
 7 int chess2[16];
 8 ///方便最后输出拨动的位置
 9 int forEasyCoutRow[16] = {1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4};
10 int forEasyCoutCol[16] = {1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4};
11 
12 void flip(int i)  ///拨锁
13 {
14     for(int j=0 ; j<16 ; j++){
15         if( j%4 == i%4  ||  j/4 == i/4 )
16             chess = chess ^ (0x1<<j) ;
17     }
18 }
19 
20 void init()
21 {
22     chess = 0;
23     char temp;
24     for(int i=0 ; i<16 ; i++){
25         scanf("%c",&temp);
26         if( temp!='+' && temp!='-' )
27             scanf("%c",&temp);
28         if( temp == '+' ){
29             chess = chess | 0x1 << i ;
30         }
31     }
32     for(int i=0 ; i<16 ; i++){
33         chess2[16] = 0;
34     }
35 }
36 
37 int main( )
38 {
39 //    freopen("in","r",stdin);
40 //    freopen("out","w",stdout);
41 
42     init();
43     for(int i=0 ; i<16 ; i++){
44         if( chess & 0x1<<i ){
45             for(int j=0 ; j<16 ; j++){
46                 if( j%4 == i%4  ||  j/4 == i/4 ){
47                     flip(j);
48                     chess2[j]++;
49                 }
50             }
51         }
52     }
53 
54     int step=0;
55     for(int i=0 ; i<16 ; i++){
56         if( chess2[i]%2 )
57             step++;
58     }
59     printf("%d\n",step);
60     for(int i=0 ; i<16 ; i++){
61         if( chess2[i]%2 ){
62             printf("%d %d\n",forEasyCoutRow[i],forEasyCoutCol[i]);
63         }
64     }
65     return 0;
66 }
poj2965 巧妙思路

感触:ACM离不开数学,数学的分析能大大精简算法,或是发现解题的方便方法。

posted @ 2014-03-01 20:32  basement_boy  阅读(251)  评论(0编辑  收藏  举报