【jzoj4824】【配对游戏】【搜索】

本文介绍了一种在流行跳棋游戏中实现动物消除的算法。通过直接搜索的方式,寻找可以消除的相同种类动物对,并记录最多的消除对数及最小路径长度总和。采用剪枝策略优化搜索过程。

题目大意

流行的跳棋游戏是在一个有m*n个方格的长方形棋盘上玩的。棋盘起初全部被动物或障碍物占满了。在一个方格中,‘X’表示一个障碍物,一个‘0’~‘9’的个位数字表示一个不同种类的动物,相同的个位数字表示相同种类的动物。一对动物只有当它们属于同一种类时才可以被消去。消去之后,他们所占的方格就成为空方格,直到游戏结束。要消去一对动物的前提条件是:这对候选动物所在的方格必须相邻,或它们之间存在一条通路。棋盘上一个方格只和其上下左右的方格相邻。一条通路是由一串相邻的空方格组成。路的长度则是通路中空方格的数目。你要输出可被消去的动物的最多对数,以及在此操作过程中,最小的通路长度总和。

解题思路

直接搜索,枚举删除一对可行的点,随便加点剪枝就可以了。

code

#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a>b)?b:a)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const inf=2147483647;
int const maxn=10000;
int n,m,ans1,ans2,cntt,q1[30][30],q2[30][30],cnt[30],dis[30][30],in[30][30][30],use[30][30],w[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
char map[100][100];
void dfs(int x,int y){
    cntt++;
    if(cntt>=400000)return;
    if(x>ans1){ans1=x,ans2=y;}
    else if((x==ans1)&&(y<ans2))ans2=y;
    int head=0,tail=0,lim=inf;
    fo(i,1,m)
        fo(j,1,n)
            if((!use[i][j])&&(cnt[map[i][j]]>1)){
                head=0;q1[x][tail=1]=i;q2[x][tail]=j;dis[x][tail]=-1;
                fo(ii,1,m)fo(jj,1,n)in[x][ii][jj]=0;in[x][i][j]=1;
                for(;head!=tail;){
                    head++;
                    fo(k,0,3)
                        if(use[q1[x][head]+w[k][0]][q2[x][head]+w[k][1]]){
                            if((!in[x][q1[x][head]+w[k][0]][q2[x][head]+w[k][1]])&&(dis[x][head]+1<lim)){
                                q1[x][++tail]=q1[x][head]+w[k][0];
                                q2[x][tail]=q2[x][head]+w[k][1];
                                dis[x][tail]=dis[x][head]+1;
                                in[x][q1[x][head]+w[k][0]][q2[x][head]+w[k][1]]=1;
                            }
                        }else if((dis[x][head]+1<=lim)&&((i!=q1[x][head]+w[k][0])||(j!=q2[x][head]+w[k][1]))&&(map[q1[x][head]+w[k][0]][q2[x][head]+w[k][1]]==map[i][j])){
                            lim=dis[x][head]+1;
                            head=tail;
                            break;
                        }
                }
            }
    if(lim==inf)return;
    fo(i,1,m)
        fo(j,1,n)
            if((!use[i][j])&&(cnt[map[i][j]]>1)){
                head=0;q1[x][tail=1]=i;q2[x][tail]=j;dis[x][tail]=-1;
                fo(ii,1,m)fo(jj,1,n)in[x][ii][jj]=0;in[x][i][j]=1;
                for(;head!=tail;){
                    head++;
                    fo(k,0,3)
                        if(use[q1[x][head]+w[k][0]][q2[x][head]+w[k][1]]){
                            if((!in[x][q1[x][head]+w[k][0]][q2[x][head]+w[k][1]])&&(dis[x][head]+1<=lim+2)){
                                q1[x][++tail]=q1[x][head]+w[k][0];
                                q2[x][tail]=q2[x][head]+w[k][1];
                                dis[x][tail]=dis[x][head]+1;
                                in[x][q1[x][head]+w[k][0]][q2[x][head]+w[k][1]]=1;
                            }
                        }else if((dis[x][head]+1<=lim+2)&&((i!=q1[x][head]+w[k][0])||(j!=q2[x][head]+w[k][1]))&&(map[q1[x][head]+w[k][0]][q2[x][head]+w[k][1]]==map[i][j])){
                            use[i][j]=use[q1[x][head]+w[k][0]][q2[x][head]+w[k][1]]=1;
                            cnt[map[i][j]]-=2;
                            dfs(x+1,y+dis[x][head]+1);
                            cnt[map[i][j]]+=2;
                            use[i][j]=use[q1[x][head]+w[k][0]][q2[x][head]+w[k][1]]=0;
                        }
                }
            }
}
int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d%d\n",&m,&n);
    fo(i,1,m){
        fo(j,1,n){
            scanf("%c",&map[i][j]);
            if(map[i][j]!='X'){
                map[i][j]-='0';
                cnt[map[i][j]]++;
            }else map[i][j]=10;
        }
        scanf("\n");
    }
    fo(i,0,m+1)map[i][0]=map[i][n+1]=10;
    fo(j,0,n+1)map[0][j]=map[m+1][j]=10;
    dfs(0,0);
    printf("%d %d",ans1,ans2);
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值