文章目录

  • 题目链接
  • 知识点
  • 题目列表
    • 快输
    • A - Knight Moves(BFS/双向BFS)
    • B - 迷宫(一)
    • C - 迷宫(二)(BFS)
    • D - 老子的全排列呢
    • E - [NOIP2002]选数
    • F - 素数圆环
    • G - Lake Counting(DFS找连通块)
    • H - 水图(DFS/Dijkstra)
    • I - Jelly(BFS)
    • J - N皇后问题(DFS/位运算)
    • K - [NOIP2017]棋盘(BFS+优先队列优化/DFS+记忆化优化)
    • L - 数字金字塔(DFS/DP)

题目链接

搜索专题-牛客链接
比赛密码:henau202301082000

知识点

搜索剪枝
双向搜索

题目列表

快输

import java.io.*;
import java.util.*;public class Main {public static void main(String[] args) {out.flush();}static class FastReader{BufferedReader br;StringTokenizer st;String tmp;public FastReader() {br=new BufferedReader(new InputStreamReader(System.in));}String next() {while(st==null||!st.hasMoreElements()) {try {st=new StringTokenizer(br.readLine());}catch(IOException e) {e.printStackTrace();}}return st.nextToken();}int nextInt() {return Integer.parseInt(next());}long nextLong(){return Long.parseLong(next());}String nextLine() {String str="";try {str=br.readLine();}catch(IOException e) {e.printStackTrace();}return str;}boolean hasNext(){if(st!=null&&st.hasMoreTokens())return true;try {tmp=br.readLine();st=new StringTokenizer(tmp);}catch(IOException e) {return false;}return true;}}static PrintWriter out=new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));static FastReader sc=new FastReader();
}

A - Knight Moves(BFS/双向BFS)

题意是找骑士从一个点行进到另外一个点的最少步数,可以用广搜直接找最优解,比深搜用时更少。另外还需要创建一个骑士类,来保存骑士在行进过程中的状态。

BFS解法

//骑士类
class Node{int x,y,step;//当前位置 (x,y) & 行进的步数Node(int x,int y,int step){this.x=x;this.y=y;this.step=step;}
}
 static int n,l,sx,sy,ex,ey;//骑士数量,棋盘大小,起始位置,结束位置static int dx[]=new int[]{-1,-1,1,1,-2,-2,2,2};static int dy[]=new int[]{2,-2,2,-2,1,-1,1,-1};static int vis[][];//该位置是否已访问static Queue<Node> q;//存储一个骑士在找最优解过程的所有状态public static void main(String[] args) {n=sc.nextInt();while(n-->0) {l=sc.nextInt();sx=sc.nextInt();sy=sc.nextInt();ex=sc.nextInt();ey=sc.nextInt();vis=new int[l][l];//每个骑士都需要new一个新队列来保存状态//也可以用数组来模拟队列,两个指针分别指向首尾q=new ArrayDeque<>();bfs(sx,sy,ex,ey);out.flush();}}//深搜易超时,用广搜找最优解static void bfs(int sx,int sy,int ex,int ey) {vis[sx][sy]=1;q.add(new Node(sx,sy,0));Node no;int x,y,step;//中间变量while(!q.isEmpty()) {no=q.poll();x=no.x;y=no.y;step=no.step;//找到则为最优解(此时step值最小)if(x==ex&&y==ey) {out.println(step);break;}//骑士可从该位置向8个方向行进,分别将对应状态存入队列中for(int i=0;i<8;i++) {int nx=x+dx[i];int ny=y+dy[i];//对于超出临界位置或者已经遍历过的位置(重复遍历意味着不可能达到最小值)跳过if(nx<0||nx>=l||ny<0||ny>=l||vis[nx][ny]==1) continue;//该位置遍历过,需要vis数组的状态vis[nx][ny]=1;q.add(new Node(nx,ny,step+1));}}}

BFS-数组模拟队列

    static node q[]=new node[100005];//存储所有状态下的骑士static void bfs(int sx,int sy,int ex,int ey) {int head=1,tail=1;vis[sx][sy]=1;q[tail]=new node(sx,sy,0);tail++;while(head<tail){int x=q[head].x;int y=q[head].y;int step=q[head].step;//找到即为最优解(step值最小)if(x==ex&&y==ey){out.printf("%d\n",step);out.flush();break;}for(int i=0;i<8;i++){int nx=x+dx[i];int ny=y+dy[i];if(nx>=0&&nx<n&&ny>=0&&ny<n&&vis[nx][ny]==0){vis[nx][ny]=1;q[tail]=new node(nx,ny,step+1);tail++;}}head++;}}

双向BFS
这个解法就是指,从起点和终点同时出发进行搜索,二者第一次相遇的位置对应的步数即为最优解;即原本的解答树规模是 an,使用双向搜索后,规模立刻缩小到了约 2an/2 ,当n较大时优化非常可观。

浅测了一下,对于本题并没有多少优化,但是也能过。

最优解为第一次分别从起点或终点到达碰撞位置所用步数之和,需要加一个数组dis更新从终点或起点到达该碰撞位置。

以及骑士类与上相同,不再贴。

 static int n,l,sx,sy,ex,ey;//骑士数量,棋盘大小,起始位置,结束位置static int dx[]=new int[]{-1,-1,1,1,-2,-2,2,2};static int dy[]=new int[]{2,-2,2,-2,1,-1,1,-1};static int vis[][];//分别标记从起点/终点出发是否已访问该位置,从起点访问标记1,否则标2static int dis[][];//更新从对面位置出发到该碰撞点走过的距离static Queue<Node> qs,qe;//存储一个骑士在从起点/终点出发找最优解过程的所有状态public static void main(String[] args) {n=sc.nextInt();while(n-->0) {l=sc.nextInt();sx=sc.nextInt();sy=sc.nextInt();ex=sc.nextInt();ey=sc.nextInt();vis=new int[l][l];dis=new int[l][l];qe=new ArrayDeque<>();qs=new ArrayDeque<>();if(sx==ex&&sy==ey)out.println(0);else bfs(sx,sy,ex,ey);out.flush();}}static void bfs(int sx,int sy,int ex,int ey) {vis[sx][sy]=1;vis[ex][ey]=2;qs.add(new Node(sx,sy,0));qe.add(new Node(ex,ey,0));Node no;int now=0;//now表示为当前遍历回合数(保证取出的是同一行进距离的骑士)//从起点/终点同时出发,有一个队列为空则停止c:while(!qs.isEmpty()&&!qe.isEmpty()) {while(qs.peek().step==now){no=qs.poll();for(int i=0;i<8;i++){int nx=no.x+dx[i];int ny=no.y+dy[i];if(nx<0||nx>=l||ny<0||ny>=l||vis[nx][ny]==1) continue;//从终点出发遍历过该位置,此时找到最优解(step值最小)if(vis[nx][ny]==2){out.println(no.step+1+dis[nx][ny]);break c;}qs.add(new Node(nx,ny,no.step+1));vis[nx][ny]=1;dis[nx][ny]=no.step+1;}}while(qe.peek().step==now) {no=qe.poll();for(int i=0;i<8;i++){int nx=no.x+dx[i];int ny=no.y+dy[i];if(nx<0||nx>=l||ny<0||ny>=l||vis[nx][ny]==2) continue;//从起点出发遍历过该位置,此时找到最优解(step值最小)if(vis[nx][ny]==2){out.println(no.step+1+dis[nx][ny]);break c;}qe.add(new Node(nx,ny,no.step+1));vis[nx][ny]=2;dis[nx][ny]=no.step+1;}}now++;}}

算法对比(测试平台Vjudge)

B - 迷宫(一)

直接深搜找可行解

 static int n,m;//棋盘大小static int dx[]={-1,1,0,0};static int dy[]={0,0,-1,1};static int[][] mp;//存地图static boolean f=false;//判断能否顺利走出迷宫public static void main(String[] args) {n=sc.nextInt();m=sc.nextInt();mp=new int[n][m];for(int i=0;i<n;i++) {for(int j=0;j<m;j++) {mp[i][j]=sc.nextInt();}}dfs(0,0);out.println(f?"YES":"NO");out.flush();}//深搜找可行解static void dfs(int x,int y) {//(x,y)表示当前位置//排除临界情况if (x<0||x>=n||y<0||y>=m||mp[x][y]==1)return;//搜索到目标直接返回if (x==n-1&&y==m-1) {f=true;return;}mp[x][y]=1;//地图内可直接进行标记,经过该点标记为1for(int i=0;i<4;i++){int nx=x+dx[i];int ny=y+dy[i];dfs(nx,ny);}}

C - 迷宫(二)(BFS)

跟A题类似,除了多了一个关于钥匙开门的条件,只需要注意遍历该位置时是否带着钥匙属于不同的状态。

 static int H,W,sx,sy;//迷宫大小,起始位置static int dx[]={-1,1,0,0};static int dy[]={0,0,-1,1};static char[][] mp;//存迷宫static int vis[][][];//该位置是否已访问,第三个维度表示走过该位置是否拿着钥匙static Queue<Node> q;//存储找最优解过程的所有状态public static void main(String[] args) {H=sc.nextInt();W=sc.nextInt();mp=new char[H][W];vis=new int[H][W][2];q=new ArrayDeque<>();for(int i=0;i<H;i++){mp[i]=sc.next().toCharArray();for(int j=0;j<W;j++) {if(mp[i][j]=='S') {sx=i;sy=j;} }} out.println(bfs(sx,sy));out.flush();}//广搜找最优解static int bfs(int sx,int sy) {vis[sx][sy][0]=1;//标记起点状态q.add(new Node(sx,sy,0,0));Node no;while(!q.isEmpty()){no=q.poll();int x=no.x,y=no.y,key=no.key,step=no.step;//找到终点,即找到最优解if(mp[x][y]=='E') return step;for(int i=0;i<4;i++) {int nx=dx[i]+x;int ny=dy[i]+y;//临界状况跳过if(nx<0||nx>=H||ny<0||ny>=W)continue;if(vis[nx][ny][key]==1||mp[nx][ny]=='W'||(mp[nx][ny]=='D'&&key==0))continue;vis[nx][ny][key]=1;q.add(new Node(nx,ny,mp[nx][ny]=='K'?1:key,step+1));//拿到钥匙则钥匙数标记为1,否则钥匙数不变}}//没有找到通路return -1;}

D - 老子的全排列呢

深搜过程回溯状态是因为,该题目的是求所有可行解,而判断状态的数组(pd)需要更新为当前状态。

    static int n=8;//1-8全排列static int[] pd=new int[n],used=new int[n];//判断某未数字是否已使用,存储当前方案的一个排列public static void main(String[] args) throws IOException{dfs(0);}static void dfs(int k) {//k为选择的第几个数字if(k>=n) {//条件为k==n即可,已选择n个数字,则输出该排列out.print(used[0]);for(int i=1;i<n;i++)out.print(" "+used[i]);out.println();out.flush();return;}else {for(int i=0;i<n;i++) {if(pd[i]==0) {pd[i]=1;used[k]=i+1;dfs(k+1);//遍历所有方案,回溯pd[i]=0;}}}}

E - [NOIP2002]选数

解法一:dfs常规解法

 static int n,k,ans=0;//共n个数,选k个,和为素数的种类数static int a[];//存储数列public static void main(String[] args) {n=sc.nextInt();k=sc.nextInt();a=new int[n];for(int i=0;i<n;i++)a[i]=sc.nextInt();dfs(0,0,0);out.println(ans);out.flush();}//深搜求所有可行解static void dfs(int cnt,int sum,int start){/** @param* cnt代表现在选择了多少个数* sum表示当前的和* start表示选数起始值*///选够k个数if(cnt==k){if(isprime(sum))ans++;//和为素数,结果+1return ;}//步数要加一,和也要加//选数起始值要变成i+1,以免算重for(int i=start;i<n;i++)dfs(cnt+1,sum+a[i],i+1);}//判断是否为质数static boolean isprime(int n){for(int i=2;i*i<=n;i++){if(n%i==0)return false;}return true;}

解法二:
在搜索过程中直接求素数种类总数,即将深搜目的改为返回当前素数之和是否为质数,是质数返回1,相当于返回:存在一种素数和,结果+1;

 static int n,k,ans=0;static int a[];public static void main(String[] args) throws IOException{// TODO Auto-generated method stubn=sc.nextInt();k=sc.nextInt();a=new int[n];for(int i=0;i<n;i++)a[i]=sc.nextInt();out.println(dfs(k,0,0));out.flush();}static int dfs(int k,int sum,int start){//k为还需要选中的个数,sum为前面累加的和//start和end为全组合剩下数字的选取范围;调用递归生成全组合//在过程中逐渐把K个数相加,当选取的数个数为0时,直接返回前面的累加和是否为质数即可if(k==0)return isprime(sum)?1:0;int cnt=0;for(int i=start;i<n;i++){cnt+=dfs(k-1,sum+a[i],i+1);}return cnt;}//判断是否质数static boolean isprime(int n){for(int i=2;i*i<=n;i++){if(n%i==0)return false;}return true;}

F - 素数圆环

输入的整数不超过19,最大相邻数字之和不超过37,可以直接用存储1-37是否为素数的数组进行判断。

 static int n,cnt=0;//输入数据,case数static boolean[] vis;//vis[i] : 标记数字i是否已访问static int[] ans;//存答案序列//判断素数(0~39)static int isprime[]={0,0,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0};public static void main(String[] args) throws IOException{while(sc.hasNext()) {out.printf("Case %d:\n",++cnt);n=sc.nextInt();//下标从1开始vis=new boolean[n+1];ans=new int[n+1];ans[1]=1;dfs(2);//从第2个数开始,第一个数为1out.println();//case之间存在空行out.flush();}}//深搜求所有可行解static void dfs(int k){//k指要选择的数字是第几个数//选够n个数,进行输出if(k>n) {//判断首尾元素之和是否为素数if(isprime[1+ans[k-1]]==1) {out.print(ans[1]);for(int i=2;i<=n;i++)out.printf(" %d",ans[i]);out.println();}return;}//从数字2开始判断for(int i=2;i<=n;i++) {//如果这个数字访问过或者跟上一个写入的数字之和为非素数,则跳过if(vis[i]||isprime[i+ans[k-1]]==0)continue;vis[i]=true;ans[k]=i;dfs(k+1);//遍历所有方案,回溯vis[i]=false;}}

G - Lake Counting(DFS找连通块)

题目意思就是找出有多少个连通区域。按照题目要求,如果某点为’W’(池塘),则其八个方向上同为池塘的点都算与之相连。对于搜索过的点,直接将其在数组中转为’.'(旱地),之后就不会重复搜索。

 static int n,m,ans=0;//农田大小,水田总数static char mp[][];//存储图public static void main(String[] args) throws IOException{n=sc.nextInt();m=sc.nextInt();mp=new char[n][m];for(int i=0;i<n;i++){mp[i]=(sc.next()).toCharArray();}for(int i=0;i<n;i++){for(int j=0;j<m;j++){//每一次搜索将联通的一片区域转为旱地if(mp[i][j]=='W'){ans++;//有水方格,结果就+1dfs(i,j);//传入该点坐标进行搜索}}}out.println(ans);out.flush();}public static void dfs(int x,int y){int nx,ny;//总共搜索9个点,包括本身这个点//为保证不重复,搜过就将该点在mp数组中标记为'.'for(int i=-1;i<=1;i++){for(int j=-1;j<=1;j++){nx=x+i;//横坐标ny=y+j;//纵坐标if(nx>=0&&nx<n&&ny>=0&&ny<m&&mp[nx][ny]=='W'){mp[nx][ny]='.';//如果该点是'W'的话,就令该点是 '.' 以后也不访问它dfs(nx,ny);// 去搜索该点}}}}

H - 水图(DFS/Dijkstra)

思路:由题目可知具有n个点和n-1条边的无向联通图是一棵树(如下图所示)。

当树具有不同的分支,则要想求从某个点出发经过该图中的每个点至少一次的最小距离,一个思路是找一条从起点出发到所有点中的一条最长路径,用所有边的权重之和(sum)的两倍减去这条路径的长度(maxLen),得到就是每个点至少经过一次所需要的最小距离(ans),即:
            ans = sum*2 - maxLen

如下例中,sum=1+2+3=6,maxLen=1+3=4,则ans=6*2-4=8,即路径1->2->3->2->4;

解法一:
DFS解法,Vector存邻接表,深搜求最长路径(maxLen)

//节点类
class Node{int v,w;//该边的另一个点,边的权重Node(int v,int w){this.v=v;this.w=w;}
}
 static int n,x;//点数,小w所处的位置(起始点)//存邻接表static Vector<Node>[] g; public static void main(String[] args) {n=sc.nextInt();x=sc.nextInt();long ans=0;g=new Vector[n+1];//点的标记从1开始for(int i=1;i<=n;i++) g[i]=new Vector<>();for(int i=1;i<n;i++) {int u=sc.nextInt(),v=sc.nextInt(),w=sc.nextInt();g[u].add(new Node(v,w));g[v].add(new Node(u,w));ans+=2*w;}out.println(ans-dfs(x,0));out.flush();}//深搜找从起始点出发,距离最长的一条路径(不一定经过所有点)static long dfs(int x,int fa){//注意每一次搜索都要重置ans,这样返回给最终结果时才不会求重复long ans=0;for(Node no:g[x]){if(no.v!=fa){ans=Math.max(ans,dfs(no.v,x)+no.w);}}return ans;}

解法二:Dijkstra
Dijkstra算法目的是为了找到起点到其它所有顶点之间的最短距离,具体实现就是利用贪心思想,每次从未遍历的点中找距离顶点最近的点加入已遍历顶点集合,其中会维护一个数组(d)存储所有点到顶点的最短距离。

本题中也可以利用Dijkstra算法求最长路径,只需要在遍历找最短路径时,将路径实际长度以相反数(正数中较小值,反而变成负数中较大值)的方式存入距离类来进行遍历,而d数组存储的依然是实际的路径长度。

//节点类
class Node{int v,w;//该边的另一个点,边的权重Node(int v,int w){this.v=v;this.w=w;}
}
//距离类
class Dis{int dis,idx;//距离,顶点下标Dis(int dis,int idx){this.dis=dis;this.idx=idx;}
}
 static int maxn=(int)1e5;static int n,x;//点数,小w所处的位置(起始点)//存邻接表static Vector<Node>[] g; static int[] d=new int[maxn];public static void main(String[] args) {n=sc.nextInt();x=sc.nextInt();long sum=0;d=new int[n+1];//起初均标记为1e9,逐步更新距离,方便找最小值Arrays.fill(d,(int)1e9);d[x]=0;//起点到起点距离为0g=new Vector[n+1];//点的标记从1开始for(int i=1;i<=n;i++) g[i]=new Vector<>();for(int i=1;i<n;i++) {int u=sc.nextInt(),v=sc.nextInt(),w=sc.nextInt();g[u].add(new Node(v,w));g[v].add(new Node(u,w));sum+=w;}Dijkstra();int mx=0;//找最大路径距离for(int i=1;i<=n;i++)mx=Math.max(mx,d[i]);out.println(sum*2-mx);out.flush();}static void Dijkstra(){Queue<Dis> q=new ArrayDeque<>();q.add(new Dis(-d[x],x));while(!q.isEmpty()) {int now=q.poll().idx;for(Node no:g[now]){int v=no.v;/** 说明:* ①某点到起点的距离最小,则与之相连的点到起点的距离也应最小,*  利用上面的思想,不断更新数组d的值* ②数组d中的值依然是点到起点的最大距离:*  因为遍历过程中是以-d[v]进行标记寻找*/ if(d[v]>d[now]+no.w){d[v]=d[now]+no.w;q.add(new Dis(-d[v],v));}}}}

I - Jelly(BFS)

BFS求最优解问题。维护一个dist数组存储行进步数,也是吃到的果冻数,而当第一次访问终点时求得的值即为最优值。

//坐标类
class Node{int x,y,z;Node(int x,int y,int z){this.x=x;this.y=y;this.z=z;}
}
 static int dist[][][],n;static int dx[]={-1,0,1,0,0,0},dy[]={0,1,0,-1,0,0},dz[]={0,0,0,0,1,-1};static char ch[][][];static Queue<Node> q=new ArrayDeque<>();public static void main(String[] args) {n=sc.nextInt();dist=new int[n+1][n+1][n+1];ch=new char[n+1][n+1][n+1];for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)ch[i][j]=(" "+sc.next()).toCharArray();out.println(bfs());;out.flush();}static int bfs() {for(int i=1;i<=n;i++) {for(int j=1;j<=n;j++) {Arrays.fill(dist[i][j],-1);}}q.add(new Node(1,1,1));dist[1][1][1]=1;Node no;while(!q.isEmpty()){no=q.poll();for(int i=0;i<6;i++){int x=no.x+dx[i],y=no.y+dy[i],z=no.z+dz[i];//临界情况1:超出范围if(x<=0||x>n||y<=0||y>n||z<=0||z>n)continue;//临界情况2:遇到障碍或者遍历过该点//后者可以保证所求结果为第一次访问求得的值,根据BFS性质可知为最优值if(ch[x][y][z]=='*'||dist[x][y][z]!=-1)continue;dist[x][y][z]=dist[no.x][no.y][no.z]+1;q.add(new Node(x,y,z)); }}return dist[n][n][n];}

J - N皇后问题(DFS/位运算)

回溯法:
如果当前位置放置了一个皇后,则用三个数组分别标记对应列、对角线、反对角线的状态,标记不能在相应位置放置皇后;为得到所有可行解,需回溯这三个数组的状态。

 static int n,sum=0;//棋盘大小//存储空间要设置为棋盘大小的两倍,防止超出数组存储范围static int a[]=new int[25],b[]=new int[25],c[]=new int[25],d[]=new int[25];//行,列,对角线,反对角线public static void main(String[] args) {// TODO Auto-generated method stubn=sc.nextInt();dfs(1);//得到所有结果out.println(sum);out.flush();}public static void dfs(int k){//第k行//选够n个位置if(k>n){sum++;return;}else{for(int i=1;i<=n;i++){if(b[i]==0&&c[k+i]==0&&d[k+n-i]==0){a[k]=i;//标记第k行取第i个数b[i]=1;c[k+i]=1;d[k+n-i]=1;dfs(k+1);//回溯状态b[i]=0;c[k+i]=0;d[k+n-i]=0;}}}}

位运算解法:
解析在这,是力扣上的一个解法,思路太强了,解析也很详尽,蒟蒻还需要继续学习加深理解(╥ω╥`)

 static int size,sum=0;public static void main(String[] args) {// TODO Auto-generated method stubint n=sc.nextInt();//棋盘大小 out.println(totalNQueens(n));out.flush();}static int totalNQueens(int n) {sum=0;size=(1<<n)-1;solve(0,0,0);return sum;}static void solve(int row, int na, int pie) {if (row==size) {sum++;return;}int pos=size&(~(row|na|pie));while (pos!=0) {int p=pos&(-pos);pos-=p;solve(row|p,(na|p)<<1,(pie|p)>>1);}}

K - [NOIP2017]棋盘(BFS+优先队列优化/DFS+记忆化优化)

BFS+优先队列优化:
对本题进行分析,得到从当前位置行进方向的所有种可能(用数组dx,dy存储)以及对应方向所需要付出的魔法代价;再结合下一个格子的颜色是否不同,判断是否需要付出额外代价1;最后一个格子关于有无颜色颜色再分出两种情况。

因为行进的方向不同,也就可能具有不同的代价,所以为使BFS找到的依然是最优解,利用优先队列存储行进到每个格子需要的代价,这样在更新某个位置的代价时,就能保证为最优值。

下面代码的参考解析在这。

//可排序的节点类
class Node implements Comparable<Node>{int x,y,c,w;//(x,y)为当前位置,c为颜色Node(int x,int y,int c,int w){this.x=x;this.y=y;this.c=c;this.w=w;}Node(){}@Overridepublic int compareTo(Node o) {// TODO Auto-generated method stubreturn this.w-o.w;//升序}
}
 static int inf=0x3f3f3f3f;static PriorityQueue<Node> q=new PriorityQueue<>();static int dx[]={0,1,0,-1,1,1,-1,-1,0,2,0,-2};//12个行进方向 static int dy[]={1,0,-1,0,1,-1,1,-1,2,0,-2,0};static int dw[]={0,0,0,0,2,2,2,2,2,2,2,2};//相应魔法代价static int[][] a,dis;//a存储棋盘上格子的颜色,dis存储代价static int n,m;public static void main(String[] args) {int x,y,c;//坐标点及各自颜色,有颜色的格子标记为1或2m=sc.nextInt();//棋盘的大小n=sc.nextInt();//盘上有颜色的格子的数量a=new int[m+1][m+1];dis=new int[m+1][m+1];for(int i=1;i<=n;i++){x=sc.nextInt();y=sc.nextInt();c=sc.nextInt();a[x][y]=c+1;}//这里c+1,为了方便区分无色格子 bfs();if(a[m][m]==0){//处理(m,m)没有颜色的情况int ans=Math.min(dis[m][m-1],dis[m-1][m])+2;if(ans>=inf)out.println(-1);else out.println(ans);}else{//处理(m,m),即终点格子有颜色的情况  if(dis[m][m]==inf)out.println(-1);else out.println(dis[m][m]);}out.flush();}static void bfs(){for(int i=0;i<=m;i++)Arrays.fill(dis[i],inf);dis[1][1]=0;//Node(x,y,c,w)q.add(new Node(1,1,a[1][1],dis[1][1]));Node cur;while(!q.isEmpty()){cur=q.poll();int x=cur.x,y=cur.y,c=cur.c,w=cur.w;if(dis[x][y]<w)continue;for(int i=0;i<12;i++){int nx=x+dx[i];int ny=y+dy[i];int nw=w+dw[i];if(nx<=0||nx>m||ny<=0||ny>m)continue;//保证在棋盘范围内int nc=a[nx][ny];if(nc==0)continue;if(c!=nc)nw++;//确定下一步的信息 if(dis[nx][ny]>nw){dis[nx][ny]=nw;q.add(new Node(nx,ny,nc,nw));}}}}

DFS+记忆化优化(即 SPFA ):
直接搜索会超时,所以要加上记忆化剪枝优化:即在递归过程中用两个变量存储代价和魔法使用情况。

 static int m,n;static int inf=0x3f3f3f3f;static int[][] a,dis,dir={{0,1},{1,0},{0,-1},{-1,0}};public static void main(String[] args) {m=sc.nextInt();n=sc.nextInt();a=new int[m+1][m+1];//colordis=new int[m+1][m+1];int x,y,c;for(int i=1;i<=n;i++) {x=sc.nextInt();y=sc.nextInt();c=sc.nextInt();a[x][y]=c+1;}for(int i=0;i<=m;i++)Arrays.fill(dis[i],inf);dfs(1,1,0,false);if(dis[m][m]!=inf)out.println(dis[m][m]);//能走到棋盘右下角else out.println(-1);out.flush();}//用sum记忆行进到每个状态时所需要的代价,use标记上一格是否使用了魔法//dis数组存更新后的最小代价static void dfs(int x,int y,int sum,boolean use){if(sum>=dis[x][y]) return;//更新最小代价dis[x][y]=sum;for(int i=0;i<4;i++){int nx=x+dir[i][0],ny=y+dir[i][1];if(nx<=0||nx>m||ny<=0||ny>m) continue;if(a[nx][ny]==0){//如果格子无色if(!use) {//判断有无使用过魔法a[nx][ny]=a[x][y];dfs(nx,ny,sum+2,true);//魔法代价+2a[nx][ny]=0;}continue;}if(a[nx][ny]==a[x][y])dfs(nx,ny,sum,false);elsedfs(nx,ny,sum+1,false);//不同颜色,额外消耗代价1}}

L - 数字金字塔(DFS/DP)

DP+DFS:
相当于从金字塔底部不断向上累加最大值,金字塔顶端的值(ans[1][1])即为所求。搜索的目的就在于找到这样一条使顶端和最大的路径。

 static int n;static long[][] ans;static boolean[][] vis;public static void main(String[] args) {n=sc.nextInt();ans=new long[n+1][n+1];vis=new boolean[n+1][n+1];for(int i=1;i<=n;++i)for(int j=1;j<=i;++j)ans[i][j]=sc.nextLong();out.println(dfs(1,1));out.flush();}static long dfs(int i,int j){if(vis[i][j]||i==n)return ans[i][j];ans[i][j]=ans[i][j]+Math.max(dfs(i+1,j),dfs(i+1,j+1));vis[i][j]=true;return ans[i][j];}

动归:
从上往下迭代,对于当前位置来说,其上一个位置只能为(i-1,j-1)或(i-1,j);而迭代到最后一行,这一行的值不会再随着遍历本行而更新,其对应的值含义即指以当前位置(最后一行)为结尾的路径的最大和,因此判断出其中的最大值即为所求。

 static int n;static long[][] ans;static boolean[][] vis;public static void main(String[] args) {n=sc.nextInt();ans=new long[n+1][n+1];vis=new boolean[n+1][n+1];for(int i=1;i<=n;++i)for(int j=1;j<=i;++j)ans[i][j]=sc.nextLong();long max=0;for(int i=2;i<=n;i++)for(int j=1;j<=i;j++){if(j==i)ans[i][j]+=ans[i-1][j-1];else if(j>1) ans[i][j]+=Math.max(ans[i-1][j-1],ans[i-1][j]);else ans[i][j]+=ans[i-1][j];if(i==n&&max<ans[i][j])max=ans[i][j];}out.println(max);out.flush();}

【寒假训练/Java】搜索专题相关推荐

  1. 2019级寒假训练-Java

    文章目录 选择题 单选题 编程题 7-1 整型数除法异常处理 (10 分) 7-2 使用HashMap实现查找功能 (10 分) 7-3 统计字母和空格出现的次数 (10 分) 7-4 自定义异常Ci ...

  2. [XUPT]2020寒假训练---比赛专题

    比赛链接:https://vjudge.net/contest/357216 说明: 比赛难度正好符合寒假训练的同学.(有一两道可能一些同学做过,我们出题没考虑到sorry) 下面对题目进行解答一下. ...

  3. 20200203DLUT寒假训练赛div2-简单搜索专场

    20200203DLUT寒假训练赛div2-简单搜索专场 :比赛地址 A - Find The Multiple 简单的dfs水题,主要是取10x和10x+1两种情况,并且注意函数存储数字必须得用un ...

  4. 牛客网平台常州大学新生寒假训练会试

    A-添加逗号 链接:https://www.nowcoder.net/acm/contest/78/A 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其 ...

  5. 2019.6.7 一场搜索专题的考试【including 洛谷·血色先锋队,入门OJ·兴建高铁,珠光宝气阁

    这次分数还好.但全是搜索题还没上200就有点打击人了--[本狸才177QAQ 血色先锋队/血色敢死队 传送门:洛谷P1332 & 入门OJ P2259 Description 邪魔天国领主复活 ...

  6. NEFU 大一寒假训练十二(set)2020.02.18

    Summary 可能是昨天的题少了一些,今天的题多了一堆,还疯狂TLE /(ㄒoㄒ)\~~ Information No. Title AC/Submit A 明明的随机数-set 60/101 B ...

  7. JAVA集合专题+源码分析

    文章目录 Java集合专题 集合和数组的区别 数组 集合 区别 集合体系结构介绍 单列集合 [Collection ] Collection接口 迭代器 迭代器原理 增强for循环 List接口 对集 ...

  8. 寒假训练八(优先队列)2020.02.14(7题)

    寒假训练八(优先队列)id:530 Problem:A 买饭-优先队列 Description 林大食堂非常拥挤,得排队买饭,陈老师也是一样的! 有n个人在一个卖饭窗口前排队买饭,假如每个人买饭的时间 ...

  9. 一起开心2020蓝桥寒假训练(二)7-6 彩虹瓶 (25分)用到栈,队列

    一起开心2020蓝桥寒假训练(二)7-6 彩虹瓶 (25分) 彩虹瓶的制作过程(并不)是这样的:先把一大批空瓶铺放在装填场地上,然后按照一定的顺序将每种颜色的小球均匀撒到这批瓶子里. 假设彩虹瓶里要按 ...

最新文章

  1. Gitlab代码托管服务器安装
  2. 使用tensorflow出现 ImportError: DLL load failed: 找不到指定的程序
  3. css优先级和权重问题
  4. 《FPGA入门教程》看书随笔——数字电路设计入门
  5. 重写equals方法---java
  6. KMP算法详解P3375 【模板】KMP字符串匹配题解
  7. information_schema.engines学习
  8. 数字信号中的各种频率
  9. 粉丝福利,抽5本《新程序员》004期免费送
  10. Android逆向分析案例——某点评APP登陆请求数据解密
  11. Landesk桌面管理之服务器管理篇
  12. 绝对不变性原理、内模原理
  13. 手机电源键关不了屏幕_手机死机关不了机怎么办
  14. Steven-Java-练习(1)
  15. php 生成PDF文件
  16. 2020年计算机专项技能鉴定考试---四川省长宁县职业技术学校
  17. java弹弓类游戏_Android cocos2d 弹弓游戏 Catapult 源代码
  18. 【第三篇】微信小程序云开发项目总结
  19. IntelliJ IDEA安装破解图文教程
  20. Python中的多线程(史上最简单易懂版)

热门文章

  1. iphone的计算机误删,iphone短信误删如何恢复?这样做快速恢复
  2. upc 6445: 棋盘V (网络流费用流解决匹配问题)
  3. linux下php获取shell命令返回数据
  4. GDB调试工具使用总结
  5. Centos清除登录痕迹
  6. 干货!360全景拍摄中相机设置的标准程序
  7. [蜂群课堂]懒人精灵自动化进阶课程
  8. 5款Android版Office简评
  9. 【小程序】PCM音频录制播放小工具
  10. Android定制实现上网限制iptables