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*搜索,细节