目录
题目链接:https://vjudge.net/contest/451581#problem/A
题目链接:https://vjudge.net/contest/451581#problem/B
题目链接:https://vjudge.net/contest/451581#problem/C
题目链接:https://vjudge.net/contest/451581#problem/D
题目链接:https://vjudge.net/contest/451581#problem/E
题目链接:https://vjudge.net/contest/451581#problem/F
题目链接:https://vjudge.net/contest/451581#problem/G
A - Breadth First Search
题目链接:HRBU 2021年暑期训练阶段二Day1 - Virtual Judge
题意:要求你运用广度优先搜索算法来求出,每个顶点到达根位置(顶点1)的最短距离
做法:BFS(广度优先搜索)简单来说就是,在遍历某一个数据的相邻边时,我先把这个边的所有相邻边遍历结束后,在去遍历下一个点
图示
第一步:先遍历1这个根节点的第一个相邻点2
第二步:再遍历1这个根节点的第二个相邻点4
第三步:根节点1的相邻点遍历结束,去遍历2这个节点,发现这个点的所有相邻节点都已经遍历过了所以直接去遍历4这个节点,发现只剩下一个相邻节点3还没有遍历,所以遍历3这个节点,随后遍历结束,输出结果。
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<string> #include<string.h> #include<cstdlib> #include<fstream> #include<queue> #include<stack> #include<map> #include<set> using namespace std; typedef long long ll; const int maxn=6; int mp[maxn][maxn]; bool vis[maxn][maxn]; int sx,sy,ex,ey; int t,u,n,x; int dir[4][2]= {{0,1},{1,0},{0,-1},{-1,0}}; int a[110][110],s[110]; void bfs(int b) { memset(s,-1,sizeof(s)); queue<int> Q; Q.push(b); s[b]=0; while(!Q.empty()) { int now=Q.front(); //cout<<"now:"<<now<<endl; Q.pop(); //int cnt=0; //cout<<"cnt:"<<cnt<<endl; for(int i=1; i<=t; i++) { if(s[i]!=-1) continue; if(a[now][i]==1) Q.push(i),s[i]=s[now]+1;//cnt++; } //cout<<"cnt:"<<cnt<<endl; } } int main() { cin>>t; for(int j=1; j<=t; j++) { cin>>u>>n; for(int i=1; i<=n; i++) cin>>x,a[u][x]=1; /*for(int i=1; i<=t; i++) { for(int j=1; j<=4; j++) cout<<a[i][j]<<" "; cout<<endl; }*/ } bfs(1); /*for(int i=1;i<=t;i++) cout<<s[i]<<endl;*/ for(int i=1; i<=t; i++) cout<<i<<" "<<s[i]<<endl; return 0; }
B - Depth First Search
题目链接:HRBU 2021年暑期训练阶段二Day1 - Virtual Judge
题意:要求运用深度优先搜索遍历算法求出,从根节点出发到所有顶点遍历结束再回到根节点,所有点的遍历顺序
做法:DFS(深度优先搜索)遍历某个顶点时,先将这个顶点能够遍历最长的边遍历结束,之后再回溯到起始位置,进行下一个顶点的遍历
图示
第一步:由1点出发到2,再由2到3,从3再出发到5,接着由5继续到6,
第二步:发现到6之后没有其余的相邻节点了,所以开始回溯,由6开始回头,到5,到3,同时发现,到3之后在2这个点还有一个4没有遍历过,于是先遍历4然后再次没有了相邻节点,继续回溯,一直回溯到初始点1
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<string> #include<string.h> #include<cstdlib> #include<fstream> #include<queue> #include<stack> #include<map> #include<set> using namespace std; typedef long long ll; const int maxn=6; int mp[maxn][maxn]; int sx,sy,ex,ey; int n,u,k,x,cnt; int dir[4][2]= {{0,1},{1,0},{0,-1},{-1,0}}; int a[110][110]; bool vis[110]; struct Node { int be; int en; }s[110]; void dfs(int b) { s[b].be=cnt++; for(int i=1;i<=n;i++) { if(a[b][i]==1&&!s[i].be) dfs(i); } s[b].en=cnt++; } int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>u>>k; for(int j=1;j<=k;j++) { cin>>x; a[i][x]=1; } } cnt=1; memset(vis,false,sizeof(vis)); for(int i=1;i<=n;i++) s[i].be=s[i].en=0; for(int i=1;i<=n;i++) { if(!s[i].be) dfs(i); } for(int i=1;i<=n;i++) cout<<i<<" "<<s[i].be<<" "<<s[i].en<<endl; return 0; }
C - 非常可乐
题目链接:HRBU 2021年暑期训练阶段二Day1 - Virtual Judge
题意:中文题面,理解不了自己的问题
做法:刚刚开始s中存放着所有的数据,n,m都是空的,要平分的过程中,当我们能做到把三个容器中一个倒空,另外两个则是相同的数据时,不就满足了我们要平分的条件了吗,其次如果一开始可乐的含量数就是奇数,这说明无论我们怎么去分,都是不可能分出相等的结果的,所以我们直接判断退出就行。3个杯子一共有3!=6次倒杯子的方法,所以我们只要bfs遍历这六个方法就行了
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define N 100+5 using namespace std; struct node{ int a,b,s,t; }cole[N],st; int a,b,s; int vis[N][N]; int bfs() { queue<node> q; memset(vis,0,sizeof(vis)); st.a=0; st.b=0; st.s=s; st.t=0; q.push(st); vis[a][b]=1; while(!q.empty()) { node u=q.front(),v; if(u.a==s/2 && u.s==s/2) return u.t; if(u.s && u.a!=a) //s->a { int c=a-u.a; if(u.s>=c) v.a=a,v.s=u.s-c; else v.a=u.a+u.s,v.s=0; v.b=u.b; v.t=u.t+1; if(!vis[v.a][v.b]) { q.push(v); vis[v.a][v.b]=1; } } if(u.s && u.b!=b) { int c=b-u.b; if(u.s>=c) v.b=b,v.s=u.s-c; else v.b=u.b+u.s,v.s=0; v.a=u.a; v.t=u.t+1; if(!vis[v.a][v.b]) { q.push(v); vis[v.a][v.b]=1; } } if(u.a && u.s!=s) { int c=s-u.s; if(u.a>=c) v.s=s,v.a=u.a-c; else v.s=u.s+u.a,v.a=0; v.b=u.b; v.t=u.t+1; if(!vis[v.a][v.b]) { q.push(v); vis[v.a][v.b]=1; } } if(u.a && u.b!=b) { int c=b-u.b; if(u.a>=c) v.b=b,v.a=u.a-c; else v.b=u.b+u.a,v.a=0; v.s=u.s; v.t=u.t+1; if(!vis[v.a][v.b]) { q.push(v); vis[v.a][v.b]=1; } } if(u.b && u.a!=a) { int c=a-u.a; if(u.b>=c) v.a=a,v.b=u.b-c; else v.a=u.a+u.b,v.b=0; v.s=u.s; v.t=u.t+1; if(!vis[v.a][v.b]) { q.push(v); vis[v.a][v.b]=1; } } if(u.b && u.s!=s) { int c=s-u.s; if(u.b>=c) v.s=s,v.b=u.b-c; else v.s=u.s+u.b,v.b=0; v.a=u.a; v.t=u.t+1; if(!vis[v.a][v.b]) { q.push(v); vis[v.a][v.b]=1; } } q.pop(); } return 0; } int main() { while(scanf("%d%d%d",&s,&a,&b),s||a||b) { if(s%2) { puts("NO"); continue; } if(a<b) swap(a,b); int ans=bfs(); if(ans) printf("%d\n",ans); else puts("NO"); } return 0; }
D - Amazing Mazes
题目链接:HRBU 2021年暑期训练阶段二Day1 - Virtual Judge
题意:需要从一个迷宫的右上角走到左下角,要是走不了输出0,否则的话输出最短的步数
做法:题目言简意赅,但是最大最大的难点就是在于迷宫的搭建上!这个迷宫并不是我们常规的那种一个一部的迷宫,这个迷宫相当于一种上帝视角看到的三维迷宫,因为该迷宫的墙面是立体的。因此本题的重难点并不是在于该怎么去走迷宫而是如何搭建迷宫。
首先我们要明确,这个迷宫最外围一圈是无法行走的也就是下面图示的一种情况
这表示这我们搭建的这个迷宫矩阵的最外围一圈全部都是1(表示墙壁),因此我们在矩阵初始化时将边界全部初始化为1,关于起点和终点,因为我们搭建的这个矩阵既表示墙壁也表示房间,所以0,1以及右下角结点是0是1无所谓的,这并不会影响到我们迷宫的遍历
其次,就是题目中奇偶行输入代表的意思,奇数行代表的同一行内房间的位置以及每个房间之间墙的存在情况,偶数行则代表上下方向的房间之间墙的情况;这是我们在矩阵初始化输入时就在考虑的问题,把奇数位当成房间,偶数位当成房间之间的墙,因为这个迷宫的特殊性,所以每一次遍历其实我们都是移动两个的距离,因为人是不能在墙壁中行走的。因此墙的位置存放什么数据一点都不影响最后的结果,再来一个BFS的板子遍历,最后注意一点遍历时需要特判你要到达的点的中间位置是能通行的,不然两点间根本不连通,怎么可能到达呢?
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<string> #include<string.h> #include<cstdlib> #include<fstream> #include<queue> #include<stack> #include<map> #include<set> using namespace std; typedef long long ll; const int maxn=70; int n,m,ex,ey; int dir[4][2]= {{0,2},{2,0},{0,-2},{-2,0}}; int mp[maxn][maxn]; int dis[maxn][maxn]; struct Node { int x,y; }; void make(int n,int m) { memset(mp,0,sizeof(mp)); for(int i=0;i<=2*n;i++) { if(i==0||i==2*n) { for(int j=0;j<=2*m;j++) mp[i][j]=1; } else mp[i][0]=mp[i][2*m]=1; } for(int i=1;i<=2*n-1;i++) { if(i%2==1) { for(int j=0;j<m-1;j++) cin>>mp[i][2*j+2]; } else { for(int j=0;j<m;j++) cin>>mp[i][2*j+1]; } } ex=2*n-1; ey=2*m-1; } int bfs() { queue<Node> Q; memset(dis,-1,sizeof(dis)); Q.push((Node){1,1}); dis[1][1]=0; while(!Q.empty()) { Node now=Q.front(); Q.pop(); if(now.x==ex&&now.y==ey) break; for(int i=0;i<4;i++) { int xx=now.x+dir[i][0]; int yy=now.y+dir[i][1]; if(xx>=1&&xx<=2*n&&yy>=1&&yy<2*m&&mp[(xx+now.x)/2][(yy+now.y)/2]==0&&mp[xx][yy]==0&&dis[xx][yy]==-1) { Q.push((Node){xx,yy}); dis[xx][yy]=dis[now.x][now.y]+1; } } } return dis[ex][ey]+1; } int main() { while(cin>>m>>n&&n&&m) { make(n,m); int ans=bfs(); cout<<ans<<endl; } return 0; }
E - A计划
题目链接:HRBU 2021年暑期训练阶段二Day1 - Virtual Judge
做法:三维的BFS遍历,唯一需要注意的一点就是,当踩到了传送位置时,是不计算步数的,直接送到下一层
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<string> #include<string.h> #include<cstdlib> #include<fstream> #include<queue> #include<stack> #include<map> #include<set> using namespace std; typedef long long ll; # define inf 0x3f3f3f3f # define maxn 15 int n,m,t; char mp[2][maxn][maxn]; int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; struct Node { int x,y,z,Tim; }; int bfs() { queue<Node> Q; Q.push((Node){0,1,1,0}); while(!Q.empty()) { Node now=Q.front(); Q.pop(); if(mp[now.x][now.y][now.z]=='P') return 1; else if(mp[now.x][now.y][now.z]=='*') continue; mp[now.x][now.y][now.z]='*'; for(int i=0;i<4;i++) { int xx=now.x; int yy=now.y+dir[i][0]; int zz=now.z+dir[i][1]; if(mp[xx][yy][zz]=='*'||now.Tim+1>t) continue; if(mp[xx][yy][zz]=='#') { mp[xx][yy][zz]='*'; xx=1-xx; if(mp[xx][yy][zz]=='#'||mp[xx][yy][zz]=='*') { mp[xx][yy][zz]=mp[1-xx][yy][zz]='*'; continue; } } Q.push((Node){xx,yy,zz,now.Tim+1}); } } return 0; } /*int bfs() { Node n1; n1.x = 0; n1.y = 1; n1.z = 1; n1.Tim = 0; queue<Node> q; q.push(n1); while (!q.empty()) { Node n2 = q.front(); q.pop(); if (mp[n2.x][n2.y][n2.z] == 'P') return 1; else if (mp[n2.x][n2.y][n2.z] == '*') continue; mp[n2.x][n2.y][n2.z] = '*'; for (int i = 0; i < 4; i++) { Node n3; n3.x = n2.x; n3.y = n2.y + dir[i][0]; n3.z = n2.z + dir[i][1]; n3.Tim = n2.Tim + 1; if (mp[n3.x][n3.y][n3.z] == '*' || n3.Tim > t) continue; else if (mp[n3.x][n3.y][n3.z] == '#') { mp[n3.x][n3.y][n3.z] = '*'; n3.x = 1 - n3.x; if (mp[n3.x][n3.y][n3.z] == '#' || mp[n3.x][n3.y][n3.z] == '*') { mp[n3.x][n3.y][n3.z] = mp[1 - n3.x][n3.y][n3.z] = '*'; continue; } } q.push(n3); } } return 0; }*/ int main() { int T; cin>>T; while(T--) { cin>>m>>n>>t; memset(mp,'*',sizeof(mp)); for(int i=0;i<2;i++) { for(int j=1;j<=m;j++) { for(int k=1;k<=n;k++) cin>>mp[i][j][k]; } } int ans=bfs(); if(ans) cout<<"YES"<<endl; else cout<<"NO"<<endl; } return 0; }
F - Oil Deposits
题目链接:HRBU 2021年暑期训练阶段二Day1 - Virtual Judge
题意:给定一块油田的俯视图,一个@代表一块油田,但是若这个油田的八个方向里有任意一个方向存在另一个油田的话,这两块油田算是一块油田,也就是说这是一个连通块,问你有多少个连通块。
做法:简单的深搜遍历就好了,把每一次遍历的点都直接变成 * 防止对后续深搜的回溯产生影响,需要注意一个方向,遍历的方向是8个,而不是常规的4个
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<string> #include<string.h> #include<cstdlib> #include<fstream> #include<queue> #include<stack> #include<map> #include<set> using namespace std; typedef long long ll; const int maxn=110; int n,m,cnt=0,flag; int dir[8][2]= {{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}}; char mp[maxn][maxn]; int dfs(int x,int y) { if(mp[x][y]=='*') return flag; mp[x][y]='*'; flag=1; for(int i=0;i<8;i++) { int xx=x+dir[i][0]; int yy=y+dir[i][1]; if(xx>=1&&xx<=n&&yy>=1&&yy<=m) dfs(xx,yy); } return flag; } int main() { while(cin>>n>>m) { cnt=0; if(n==0&&m==0) break; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) cin>>mp[i][j]; } //cout<<1<<endl; for(int i=1;i<=n;i++) { //cout<<"i:"<<i<<endl; for(int j=1;j<=m;j++) { flag=0; if(dfs(i,j)) cnt++; } //cout<<"i+1:"<<i+1<<endl; } //cout<<2<<endl; cout<<cnt<<endl; } return 0; }
G - DNA sequence
题目链接:HRBU 2021年暑期训练阶段二Day1 - Virtual Judge
题意:给定几个字符串,要求求出一个最短的序列,做到输入的每一个序列都是这个序列的子序列,输出长度
做法:全新的知识,IDA*(迭代深搜索)和普通的深搜多了一个限制条件,规定的一个最小的长度限制,当你的长度超过限制时,就不在遍历直接返回,然后将限制的范围加大,一直到满足所有的长度为止时,这时答案就是一个最小值
借鉴博客:hdu 1560 DNA sequence(迭代加深搜索)_TommyTT的博客-CSDN博客(没有解释IDA*但是通过对题目思路的解析其实也差不多)
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<string> #include<string.h> #include<cstdlib> #include<fstream> #include<queue> #include<stack> #include<map> #include<set> using namespace std; typedef long long ll; # define inf 0x3f3f3f3f # define maxn 10 int len[maxn]; char a[maxn][maxn]; int n; int ans,deep; int point[maxn]; int com[4]= {'A','G','C','T'}; void dfs(int t1,int temp[]) { if(t1>deep)return ; int maxx=0; for(int i=0; i<n; i++) { int t=len[i]-temp[i]; maxx=max(t,maxx); } if(maxx==0) { ans=t1; return ; } if(maxx+t1>deep)return ; for(int i=0; i<4; i++) { int flag=0; int t[10]; for(int j=0; j<n; j++) { if(a[j][temp[j]]==com[i]) { flag=1; t[j]=temp[j]+1; } else t[j]=temp[j]; } if(flag==1) { dfs(t1+1,t); } if(ans!=-1)break; } } int main() { int T; cin>>T; while(T--) { deep=0; cin>>n; int maxx=-1; for(int i=0; i<n; i++) { cin>>a[i]; int l=strlen(a[i]); len[i]=l; if(maxx<l)maxx=l; } ans=-1; while(1) { dfs(0,point); if(ans!=-1)break; deep++; } cout<<ans<<endl; } return 0; }