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

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离不开数学,数学的分析能大大精简算法,或是发现解题的方便方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值