UVa1603 - Square Destroyer

题意:

给定一个火柴棒拼成的方格阵,然后去掉一些火柴棒,问至少再去掉几根火柴棒能够让图中一个正方形都没有。

思路:

1. 由于题目中给定了 n 的范围,2 * n * (n + 1) <= 60 -> 所以能够保证所有的火柴用 __int64 的位运算表示;

2. 问题的关键在于如何生成火柴构成的方阵,以及生成方阵之后如何去搜索;

3. 启发式函数 h 的计算需要考量:如果删除了某个方阵的一个边,则能够保证 h(s1) <= h(s2) + C(s1, s2),其中 C(s1, s2) = 1,h(s1) - h(s2) <= 1,可以用反证法证明;

4. 各种位运算的范围要明确,如 1<<i 前面要加上 __int64 修饰方能得到正确的结果,这和 C 语言默认长度是 32 有关;

紫书分析:迭代加深搜索为主算法框架,搜索对象有两种,

1.每次考虑一个没有被破坏的正方形,在边界上找一根火柴拿掉,搜索对象是正方形,应先考虑小正方形,在考大打正方形,因为小正方形被破坏之后,大正方形就被破坏了,但是反过来却不一定,还可以加入最优性减枝,即把每个正方形看成一个顶点,有公共火柴的正方形连城一条边,则每个连通分量至少拿走一根火柴。

2.每次找一个至少能破坏一个正方形的火柴,然后拿掉。搜索对象是火柴,应该搜索尽量能破坏最多正方形的火柴,这需要计算出考虑每根火柴可以破坏掉多少个正方形,从小到大排序d[1],...当d[1]为1时即可停止搜索,因为此时可以直接计算出还需要的火柴个数,这个d数组也可以用于最优性减枝,找到最小的i,使得d[1]+d[2]=...+d[i]<=k(其中k为还省得正方形个数),至少还要i跟火柴。

还能用DLX算法解决。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int INFS = 0x7fffffff;
int N,C,E,bound;
__int64 square[100], base[6][6];
bool succ;

inline int __int64 getflag(int i){
    return ((__int64)1<<(i-1));
}
inline int geth(int i,int j){
    return (2*N+1)*(i-1)+j;
}
inline int getv(int i,int j){
    return (2*N+1)*(i-1)+j+N;
}
void build()
{
    C=0;
    memset(base,0,sizeof(base));
    for(int i=1;i<=N;i++){
        for(int j=1;j<=N;j++){
            base[i][j] |= getflag(geth(i,j)) | getflag(geth(i+1,j));
            base[i][j] |= getflag(getv(i,j)) | getflag(getv(i,j+1));
            square[C++] = base[i][j];
        }
    }
    for(int size=2;size<=N;size++){
        for(int i=1;i+size-1 <= N;i++){
            for(int j=1;j+size-1<=N;j++){
                square[C]=0;
                for(int a=0;a<size;a++){
                    for(int b=0;b<size;b++)
                        square[C] ^= base[i+a][j+b];
                }
                C += 1;
            }
        }
    }
}

int dfs(__int64 state,int depth){
    int h=0;
    __int64 u =0, s = state;
    for(int i=0;i<C;i++){
        if((s & square[i])==square[i]){
            h += 1;
            s ^= square[i];
            if(u==0)
                u=square[i];
        }
    }
    if(h==0){
        succ=true;
        return depth;
    }
    if(depth+h>bound){
        return depth+h;
    }
    int newbound = INFS;
    for(int i=1;i<=E;i++){
        if(u & getflag(i)){
            int b=dfs(state ^ getflag(i),depth+1);
            if(succ)
                return b;
            newbound = min(b,newbound);
        }
    }
    return newbound;
}
int main()
{
    int cases;
    scanf("%d",&cases);
    while(cases--){
        scanf("%d",&N);
        build();

        E = 2*N*(N+1);
        int k;
        __int64 state = ((__int64)1<<E) -1;

        scanf("%d",&k);
        for(int i=0;i<k;i++){
            int x;
            scanf("%d",&x);
            state ^= getflag(x);
        }

        succ=false;
        bound = 0;
        while(!succ){
            bound = dfs(state, 0);
        }

        printf("%d\n", bound);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值