2017福建夏令营Day3(搜索)

走出迷宫(maze)

【题目描述】 当你站在一个迷宫里的时候,往往会被错综复杂的道路弄得失去方向感,如果你能 得到迷宫地图,事情就会变得非常简单。 假设你已经得到了一个 n × m 的迷宫的图纸,请你找出从起点到出口的最短路。

【输入格式】 从文件 maze.in 中读入数据。 第一行是两个整数 n 和 m,表示迷宫的行数和列数。 接下来 n 行,每行一个长为 m 的字符串,表示整个迷宫的布局。字符. 表示空地, # 表示墙,S 表示起点,T 表示出口。

【输出格式】 输出到文件 maze.out 中。 输出从起点到出口最少需要走的步数。

【样例 1 输入】 3 5 T..## #.#.S #...#

【样例 1 输出】 7

【子任务】 对于 40% 的数据,保证 1 ≤ n, m ≤ 5; 对于另外 20% 的数据,地图中不包含字符 #; 对于 100% 的数据,保证 1 ≤ n, m ≤ 100,保证从起点出发一定能到达出口。

题解

搜索

 

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
char ch[105];
int n,m,ans=1e9+7;
int a[105][105],vis[105][105],way[105][105];
int dx[5]={0,0,1,0,-1},dy[5]={0,1,0,-1,0};
int xx,yy;
inline void dfs(int x,int y,int t)
{
    if(abs(xx-x)+abs(yy-y)+t>=ans)    return;
    if(a[x][y]==2)
    {
        ans=std::min(ans,t);
        return;
    }
    way[x][y]=std::min(way[x][y],t);
    vis[x][y]=1;
    for(int i=1;i<=4;i++)
    {
        int nx=x+dx[i],ny=y+dy[i];
        if(nx>0 && nx<=n && ny>0 && ny<=m && !vis[nx][ny] && a[nx][ny]!=1 && t+1<way[nx][ny])
            dfs(nx,ny,t+1);
    }
    vis[x][y]=0;
}
int main()
{
    freopen("maze.in","r",stdin);
    freopen("maze.out","w",stdout);
    int x,y;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",ch+1);
        for(int j=1;j<=m;j++)
        {
            if(ch[j]=='.')        a[i][j]=0;
            if(ch[j]=='#')        a[i][j]=1;
            if(ch[j]=='S')        x=i,y=j,a[i][j]=3;
            if(ch[j]=='T')    a[i][j]=2,xx=i,yy=j;
            way[i][j]=1e9+7;
        }
    }
    dfs(x,y,0);
    printf("%d\n",ans);
    return 0;
}

标签

深搜

 

因数游戏(fac)

【题目描述】 有 T 组询问,每组询问给定 2 个数 a, b,问从 a 变到 b 最少需要多少步,每次我们 可以把 a 加上它的一个因数或者减去它的一个因数。比如,6 可以变成 5, 7, 4, 8, 3, 9, 12。 特别地,如果步数 > 6 的话,输出 CalcFailed

【输入格式】 从文件 fac.in 中读入数据。 输入有多组测试数据。第一行 T 表示测试数据的个数。 接下来 T 行,每行两个整数来表示 a, b。

【输出格式】 输出到文件 fac.out 中。 输出 T 行,表示答案

【样例 1 输入】 3 18 11 4 4 1 100

【样例 1 输出】 2 0 CalcFailed

【子任务】 对于 20% 的数据,答案 ≤ 2; 对于 40% 的数据,答案 ≤ 3; 对于 60% 的数据,答案 ≤ 4; 对于 80% 的数据,没有 CalcFailed; 对于 100% 的数据,1 ≤ T ≤ 10, 1 ≤ a, b ≤ 108。

题解

70分
若x可以一步变成y,y一定可以一步变成x
设x=ka(a为当前因数)
ka+a=y,y=(k+1)a,y-a=x;
因此我们可以从a,b同时开始双向搜索3层,用hash判断是否出现过,出现过就可以输出答案
100分
70分基础上搜索因数可以用Miller-Rabin+Rho
即可过

hash代码用的是链表维护

也可以用set

#include<cstdio>
#include<cstring>
#include<cmath>
#define mod 5826451
using namespace std;
struct edge{
    int num,next;
}g[15000005],g2[15000005];
int head[5826452],head2[5826452];
int h1,t1,h2,t2,tot,t,n,m,ans,tot2;
int q[15000005],q2[15000005];
bool fd(int x){
    int qaq=x%mod;
    for(int i=head[qaq];i;i=g[i].next)if(x==g[i].num)return true;
    return false;
}
bool fd2(int x){
    int qaq=x%mod;
    for(int i=head2[qaq];i;i=g2[i].next)if(x==g2[i].num)return true;
    return false;
}
void ins(int x){g[++tot].next=head[x%mod];head[x%mod]=tot;g[tot].num=x;}
void ins2(int x){g2[++tot2].next=head2[x%mod];head2[x%mod]=tot2;g2[tot2].num=x;}
int main(){
    freopen("fac.in","r",stdin);
    freopen("fac.out","w",stdout);
    scanf("%d",&t);
    for(int tt=1;tt<=t;tt++){
        scanf("%d%d",&n,&m);
        memset(head,0,sizeof(head));
        memset(head2,0,sizeof(head2));
        tot=ans=tot2=0;
        bool find=false;
        int now=1;h1=t1=h2=t2=1;
        q[h1]=n,q2[h2]=m;ins2(n);ins(m);if(n==m)find=true;
        for(int i=1;i<=6;i++){
            if(find)break;
            now=!now;
            if(!now){
                int tmp=t1;
                for(int j=h1;j<=t1;j++){
                    for(int k=1;k<=sqrt(q[j]);k++){
                        if(q[j]%k==0){
                            if(!fd2(q[j]+k)){
                                q[++tmp]=q[j]+k;
                                if(!fd(q[j]+k))ins2(q[j]+k);else {find=true;ans=i;break;}
                            }
                            if(q[j]-k&&!fd2(q[j]-k)){
                                q[++tmp]=q[j]-k;
                                if(!fd(q[j]-k))ins2(q[j]-k);else {find=true;ans=i;break;}
                            }
                            if(q[j]!=1&&!fd2(q[j]+q[j]/k)){
                                q[++tmp]=q[j]+q[j]/k;
                                if(!fd(q[j]+q[j]/k))ins2(q[j]+q[j]/k);else {find=true;ans=i;break;}
                            }
                            if(q[j]-q[j]/k&&!fd2(q[j]-q[j]/k)){
                                q[++tmp]=q[j]-q[j]/k;
                                if(!fd(q[j]-q[j]/k))ins2(q[j]-q[j]/k);else {find=true;ans=i;break;}
                            }
                        }
                    }
                    if(find)break;
                }
                h1=t1+1,t1=tmp;
            }else{
                int tmp=t2;
                for(int j=h2;j<=t2;j++){
                    for(int k=1;k<=sqrt(q2[j]);k++){
                        if(q2[j]%k==0){
                            if(!fd(q2[j]+k)){
                                q2[++tmp]=q2[j]+k;
                                if(!fd2(q2[j]+k))ins(q2[j]+k);else {find=true;ans=i;break;}
                            }
                            if(q2[j]-k&&!fd(q2[j]-k)){
                                q2[++tmp]=q2[j]-k;
                                if(!fd2(q2[j]-k))ins(q2[j]-k);else {find=true;ans=i;break;}    
                            }
                            if(q2[j]!=1&&!fd(q2[j]+q2[j]/k)){
                                q2[++tmp]=q2[j]+q2[j]/k;
                                if(!fd2(q2[j]+q2[j]/k))ins(q2[j]+q2[j]/k);else {find=true;ans=i;break;}    
                            }
                            if(q2[j]-q2[j]/k&&!fd(q2[j]-q2[j]/k)){
                                q2[++tmp]=q2[j]-q2[j]/k;
                                if(!fd2(q2[j]-q2[j]/k))ins(q2[j]-q2[j]/k);else {find=true;ans=i;break;}
                            }
                        }
                    }
                    if(find)break;
                }
                h2=t2+1,t2=tmp;
            }
        }
        if(find)printf("%d\n",ans);
        else puts("CalcFailed");
    }    
    fclose(stdin);
    fclose(stdout);
}

标签

双向bfs

 

十五数码(fifteen)

【题目描述】 给出起始顺序,要求通过 0 的移动(与上下左右交换),排成以下顺序: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 【输入格式】 从文件 fifteen.in 中读入数据。 4 个数一行,共 4 行 16 个数。

【输出格式】 输出到文件 fifteen.out 中。 输出最少移动次数。如. 果. 无. 解. 输. 出. No。

【样例 1 输入】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 0 15

【样例 1 输出】 1

【样例 2 输入】 1 11 3 8 5 7 0 2 9 13 4 12 6 10 14 15

【样例 2 输出】 33

【子任务】 对于 20% 的数据,保证有解并且 Ans ≤ 12 对于 50% 的数据,保证有解并且 Ans ≤ 28 存在 10% 的数据无解 对于 100% 的数据,如果有解,Ans ≤ 50

题解
50分的A*
设置最大深度dep
当前为g(i),估价函数设为每个数与目标曼哈顿距离,若g(i)+h(i)<=dep就继续搜,之后再加大深度
优化记录上次状态不走回头路
若h(i)为0即可以输出答案

100分

位运算. i ≫ 2, i & 3

#define abs(x) (x>=0?x:-(x))

一些经常调用的小函数拿去 define 掉, 因为声明函数栈空间花 时间。

特判无解情况

二维数组改成一维,a[4][4] → a[16]

减少估价函数的计算量. 本题中的 h(n) 满足加法运算

调整 udlr 的顺序, 改变搜索序

把起始局面和目标局面对掉

反正这种想法就是脑洞很大的才想的出来

50分

#include <cstdio>  
#include <cstring>  
#include <cmath>  
#include <algorithm>
#include <iostream>
const int M = 4;
void swap(int*a,int*b){int tmp; tmp = *a; *a = *b; *b = tmp;}  
int move[4][2]={{-1,0},{0,-1},{0,1},{1,0}}, map[M][M], map2[M*M], to[16][2]= {{3,3},{0,0},{0,1}, {0,2},{0,3}, {1,0},{1,1}, {1,2}, {1,3},{2,0}, {2,1}, {2,2},{2,3},{3,0},{3,1},{3,2}};
int limit, flag = 0, length = 0;
int lunar(int a[][M])
{  
    int cost=0;  
    for(int i=0; i<M; i++)  
        for(int j=0; j<M; j++)  
        {  
            int w = map[i][j];  
            cost += abs(i-to[w][0]) + abs(j-to[w][1]);  
        }  
    return cost;  
}  
void dfs(int sx,int sy,int len,int l)
{  
    int nx,ny;  
    if(flag) return;  
    int ma = lunar(map);  
    if(len == limit)  
    {  
        if(ma == 0) 
        {  
            flag=1; length=len;  
            return;  
        }  
        else return;
    }  
    else if(len<limit && ma==0)  
    {  
        flag=1; length=len;  
        return;  
    }  
    for(int i=0; i<4; i++)  
    {  
        if(i + l == 3 && len>0) continue;      
        nx=sx + move[i][0]; ny = sy + move[i][1];  
        if(0<=nx&&nx<M && 0<=ny&&ny<M) 
        {  
            swap(&map[sx][sy],&map[nx][ny]);  
            int p=lunar(map);   
            if(p+len<=limit&&!flag)  
            {  
                dfs(nx,ny,len+1,i); 
                if(flag) return;  
            }  
            swap(&map[sx][sy], &map[nx][ny]);
        }  
    } 
    return;
}  
int main()  
{  
    freopen("fifteen.in","r",stdin);
    freopen("fifteen.out","w",stdout);
    int sx, sy;   
    for(int i=0; i<M * M; i++) 
    {  
        scanf("%d", &map2[i]);  
        if(map2[i]==0)  
        {   
            map[i/M][i%M] = 0;  
            sx = i/M; sy = i%M;  
        }  
        else  
            map[i/M][i%M] = map2[i];   
    }                                      
    limit = lunar(map);  
    while(!flag && length<=50) 
    {  
        dfs(sx, sy, 0, 0);  
        if(!flag) limit++;   
    }  
    if(flag) printf("%d\n", length); 
    else printf("No\n");   
    return 0;  
}  

标签

A*搜索,细节

posted @ 2017-08-23 15:16  小白的小白  阅读(383)  评论(0编辑  收藏  举报