码迷,mamicode.com
首页 > 其他好文 > 详细

费解的开关

时间:2019-07-17 23:09:31      阅读:222      评论:0      收藏:0      [点我收藏+]

标签:状态   操作   ace   etc   cti   str   搜索   amp   复杂度   

费解的开关

给出一个\(5\times 5\)矩形网格图,\(a[i][j]\)表示第i行第j列的数字(只能为0或者1),每次操作可以选择一个位置,对于该个位置以及其上下左右个一个位置上的数字0变成1,1变成0,询问是否能少于6次将所有数字变为1,如果能,请输出最少次数。

其实最终变为0还是变为1都无所谓,不妨把网格图中所有数字取反,这样就转换成最后数字要全部变为1,这样方便一些(接下来的行列坐标从0开始)。

\(h[i]\)为第i行的状态(第k位上1表示表示\(a[i][k]=1\),反之)。

注意到问题的异或性,不妨将这类问题称作类异或问题,这样的问题具有一个通性,即异或的交换律和结合律它也满足,于是多次进行一个位置的操作没有意义,而且操作不存在顺序,这样会大大优化我们的搜索。

因为原题是存在多组数据,而且数据组数还很多,于是接下来直接暴力组合
\(O(C_{25}^6)=177100\),会超时

超时代码

#include <iostream>
#include <cstdio>
#define il inline
#define ri register
using namespace std;
int ans,li((1<<25)-1);
il void get(char&);
void dfs(int,int,int);
template<class free>
il free Min(free,free);
int main(){char c;
    int lsy,s;scanf("%d",&lsy);
    while(lsy--){s=0;
        for(int i(0);i<25;++i)
            get(c),s|=c-48<<i;
        ans=7,dfs(0,0,s);
        if(ans==7)puts("-1");
        else printf("%d\n",ans);
    }
    return 0;
}
il void get(char&c){
    while(c=getchar(),c==' '||c=='\n'||c=='\r');
}
template<class free>
il free Min(free a,free b){
    return a<b?a:b;
}
void dfs(int a,int b,int c){
    if(b>=ans)return;
    if(a==25){
        if(c==li)ans=Min(ans,b);
        return;
    }
    dfs(a+1,b,c),c^=(1<<a);
    if(a%5)c^=(1<<a-1);
    if(a%5<4)c^=(1<<a+1);
    if(a-5>=0)c^=(1<<a-5);
    if(a+5<25)c^=(1<<a+5);
    dfs(a+1,b+1,c);
}

于是我们接下来的搜索要考虑剪支,因为是网格图问题,考虑方向有行列,对角线和矩形,我们按行处理,这样我们每一行只要枚举\(2^5=32\)次,不妨用枚举二进制代替自由组合,显然时间复杂度\(O(32^5)=33554432\),加上最优性剪支,即如果超出6或者比最优解大,那么return,这样还不够,注意到我们是按行处理的,如果处理完这一行,下一行一定要将其变为0,否则它以后再也变不回0了,于是加上这个剪支,此题才能过。

参考代码:

#include <iostream>
#include <cstdio>
#define il inline
#define ri register
using namespace std;
int ans,jm[6],tot[32];
il void get(char&);
void dfs(int,int,int,int);
int main(){char c;
    int lsy;scanf("%d",&lsy);
    for(int i(0),j;i<32;++i)
        for(j=4;j>=0;--j)
            if(i>>j&1)++tot[i];
    while(lsy--){ans=7;
        for(int i(0),j;i<5;++i)
            for(j=0,jm[i]^=jm[i];j<5;++j)
                get(c),(c-=48)^=1,jm[i]|=c<<j;
        dfs(0,0,jm[0],0);
        if(ans==7)puts("-1");
        else printf("%d\n",ans);
    }
    return 0;
}
void dfs(int h,int l,int r,int c){
    if(c>=ans)return;
    if(h==5){if(!l)ans=c;return;}
    for(int i(0),j,k;i<32;++i){
        if(h&&i^l)continue;k=r;
        for(j=4;j>=0;--j)
            if(i>>j&1){k^=1<<j;
                if(j)k^=1<<j-1;
                if(j<4)k^=1<<j+1;
            }dfs(h+1,k,jm[h+1]^i,c+tot[i]);
    }
}
il void get(char &c){
    while(c=getchar(),c==' '||c=='\n'||c=='\r');
}

费解的开关

标签:状态   操作   ace   etc   cti   str   搜索   amp   复杂度   

原文地址:https://www.cnblogs.com/a1b3c7d9/p/11204293.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!