USACO2011Open Silver Unlocking Blocks 题解

2 篇文章 0 订阅
1 篇文章 0 订阅

搜索搜索搜索!

     每次把一块积木移动一步,而且每块积木内部的相对位置是不变的,那么每次只要记录积木任意一点的位置表示状态即可.为了方便,我们可以设为每个积木包围块的左上角的点.

     如何实现?

一种想法是暴搜!可惜T了…

暴搜优化一下来个迭代加深,每次设定一个步数,可惜还是T了.

由于问题为”最小步数” ,我开始想广搜.可是如何判重?积木位置可以随意移动,状态最多为(30*30)3,数组开不了.后来发现:积木的具体位置是不重要的,也就是说,如果三个积木的相对位置不变,在图上的任何一个位置都是相同的,那么我们只要记录第2,3块积木相对于第1块积木的位置就好了.判重问题解决了,这道题也就解决了.

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<iostream>
using namespace std;
const int M=10;
const int N=3;
const int P=30;
int rx[]={1,0,-1,0},ry[]={0,1,0,-1};
int mp[N][M][M],px[N],py[N],n[N],l[N],w[N],mark[P*2][P*2][P*2][P*2],tx[M*M],ty[M*M];//mp[i][j][k]表示第i块积木相对于包围块左上角为j,k的位置有没有积木,l,w分别表示块的长,宽.mark用来判重,
struct node{
    int step;
    int nx[3],ny[3];
};
queue<node>Q;
bool finish(){
    for(int i=0;i<3;i++){// 判断三个块是否重叠 
        int ax=px[i],ay=py[i];
        int bx=ax+l[i],by=ay+w[i],ok=1;
        for(int j=i+1;j<3;j++){//r任意两个快之间不重叠 
            int cx=px[j],cy=py[j];
            int dx=cx+l[j],dy=cy+w[j];          
            if(!(dx<=ax||cy>=by||cx>=bx||dy<=ay))return false;
        }
    }
    return true;
}
bool chk(int lay,int yix,int step){
    int i,j,k,X=px[0],Y=py[0];
    int a=px[1]-X+P,b=py[1]-Y+P,c=px[2]-X+P,d=py[2]-Y+P;
    if(mark[a][b][c][d])return false;
    mark[a][b][c][d]=1;
    for(i=0;i<3;i++){
        for(j=0;j<3;j++){
            if(i==j)continue;
            for(k=0;k<l[i];k++){
                for(int p=0;p<w[i];p++){
                    if(!mp[i][k][p])continue;
                    int x=k+px[i]-px[j],y=p+py[i]-py[j];
                    if(x<0||y<0||x>=l[j]||y>=w[j]||!mp[j][x][y])continue;
                    return false;
                }
            }
        }
    }
    return true;
}
int main(){
    int i,j,k,ans=-1;
    for(i=0;i<3;i++)scanf("%d",&n[i]);
    for(i=0;i<3;i++){
        int mix=10,mxx=-1,miy=10,mxy=-1;
        for(j=0;j<n[i];j++){
            scanf("%d %d",&tx[j],&ty[j]);
            int a=tx[j],b=ty[j];
            mix=min(mix,a);
            mxx=max(mxx,a);
            miy=min(miy,b);
            mxy=max(mxy,b);
        }
        px[i]=mix;
        py[i]=miy;
        l[i]=mxx-mix+1;
        w[i]=mxy-miy+1;
        for(j=0;j<n[i];j++){
            int a=tx[j]-px[i],b=ty[j]-py[i];
            mp[i][a][b]=1;
        }
    }
    if(finish()){puts("1");return 0;}
    else {
        node tmp;
        for(i=0;i<N;i++){
            tmp.nx[i]=px[i];
            tmp.ny[i]=py[i];
        }
        tmp.step=0;
        Q.push(tmp) ;   
    }
    while(!Q.empty()){
        node tmp=Q.front();Q.pop();
        for(i=0;i<N;i++){
            px[i]=tmp.nx[i];
            py[i]=tmp.ny[i];    
        }
        if(finish()){
            ans=tmp.step;break;
        }
        for(i=0;i<N;i++){
            for(k=0;k<4;k++){
                px[i]+=rx[k],py[i]+=ry[k];
                if(px[i]<-10||px[i]>20||py[i]<-10||py[i]>20){
                    px[i]-=rx[k],py[i]-=ry[k];continue;
                }
                if(chk(i,k,tmp.step)){
                    node nex=tmp;;
                    nex.nx[i]=px[i];
                    nex.ny[i]=py[i];
                    nex.step=tmp.step+1;
                    Q.push(nex);
                }
                px[i]-=rx[k];
                py[i]-=ry[k];
            }
        }
    }
    printf("%d\n",(ans!=-1));
    return 0;
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值