2017级寒假ACM集训结训赛--官方题解
2018-02-04 16:30
447 查看
博客地址
A:SDUTOJ4142 蚂蚁花呗
AC/SUBMIT : 25/245
by:郭小冉(徐红博)
考查知识点:栈
通过简单的栈模拟,对于向左走的蚂蚁如果没有向右走的蚂蚁直接加入进栈,如果有,则比较大小,向左的蚂蚁大吃掉向右走的蚂蚁(pop),否则是被吃掉。
对于向右走的直接加入栈中。
当然如果暴力的话,姿势好一点的话也可以过,但是由于测题时就有人暴力卡过,所以特意加了一组10000只蚂蚁全是左边的数据,因此卡掉了一部分暴力的做法。
比如下边的这种就正好被卡掉
标程1(c++版):
标程2(c语言版):
B:SDUTOJ4143 蚂蚁森林
AC/SUBMIT : 0/10
by:郭小冉(徐红博)
考查知识点:排序+思维
比较考验思维的题目,应该属于这套题中思考难度最大的题目了;加了提示之后也没有人能想到(当然加完提示时剩下的时间已经比较短了)。
每只蚂蚁在木棍上都有自己的相对位置, 如果两只蚂蚁碰头就会改变自己的朝向,所以他们在木棍上的相对位置是不会改变的,给所有蚂蚁的位置标记大小并从小到大排序,设置初始数组和终态数组表示蚂蚁的状态,设置一个order数组表示蚂蚁被放置进去的顺序,在输出结果的时候根据这个顺序输出。
如果还不是很明白的话,可以看下面一组样例感受一下。
比如,有3只蚂蚁,蚂蚁1=(1, R),蚂蚁2= (3, L),蚂蚁3=(4, L),则两秒钟之后,3只蚂蚁分别为 1=(3,R)、2= (1,L)和 3= (2,L), 这是不转向的结果;
按照题意转向的话其结果应该为1=(1, L) , 2=(2, L), 3=( 3, R );即按位置排个序,就是它们对应的新位置;
关键点在于:对于蚂蚁来说, 可以把它们看成是没有区别的小点,那么只需独立计算出每只蚂蚁在T时刻的位置即可;
标程使用了STL中的排序,如果没有学过的同学可以先学习一下再尝试来解决这道题。
标程:
C:SDUTOJ4144 蚂蚁财富
AC/SUBMIT : 3/8
by:郭小冉(徐红博)
考查知识点:思维+模拟
本题的代码不是十分的复杂,但是就思考上来说也有一定的难度,所以在一开始就发了公告提示。
有些人可能觉得可以暴搜,因为每只蚂蚁只有两种情况,不过每只蚂蚁都要掉头是不是很麻烦,而且时间复杂度为2的n次幂,肯定超时。 因为所有蚂蚁是同时出发的,相遇时的两只蚂蚁用的时间是相同的,我们可以无视蚂蚁的区别,当两只蚂蚁相遇时它们保持原样交错而行。这样每只蚂蚁都是独立运动的,那么只要找每只蚂蚁掉下去的时间就行了。
标程:
D:SDUTOJ4145 蚂蚁借呗
AC/SUBMIT : 18/53
by:郭小冉(徐红博)
考查知识点:dfs搜索|模拟
本题实际上是一个十分裸的搜索题, 但是由于题目要求,因此其实只用循环模拟就可以做,由于题目设计的时候下标从零开始,题目中给出这个条件的位置也不是十分的显眼,因此导致有些人可能在这个地方遇到了坑。但只要能够枚举出八种情况,这道题就能够轻松解决。
利用二维数组模拟方格,读取数据,初始化方格状态,蚂蚁位置等。按照题目思路进行模拟。
知道了蚂蚁在哪,朝向哪,将所在格子颜色反转(!mp[x][y]),下一步就是根据格子原来的颜色转向了,因为提供的方向是英文,如果用switch进行选择不免显得繁琐,所以可以用数字0123来代替上右下左四个方向,蚂蚁转向可以就用取余运算来进行,右转就是 (s+1)%4,左转就是(s+3)%4,然后根据方向移动改变坐标即可。
标程1(搜索版):
标程2(while循环模拟):
标程3(枚举八种情况):
E:SDUTOJ4051 巨斧砍大树
AC/SUBMIT : 13/33
by:bLue(刘洋)
考查知识点:树
建立二叉树,然后在删除子树时只要将该子树的根节点置为 NULL 即可
标程:
F:SDUTOJ4146 龙王的工作
AC/SUBMIT : 30/67
by:玄黄(秦圣昭)
考查知识点:思维
实际上,这道题目是作为最简单的签到题的,只要读懂题意,那么这道题目就能够A掉。注意步兵时不移动的。由于玉将每次只能行进一格,那么要想回合数最少肯定是每一步都往步兵行进的方向去走,而但二者差别较远时肯定是斜着走最优,当二者走到同一行或者同一列时,玉将就只能直着走了,也就是说答案其实是两者横纵坐标相减,差别最大的那一个。
code:
G:SDUTOJ4141 唱歌的小P
AC/SUBMIT : 92/404
by:IceCapriccio(王炜良)
考查知识点:模拟
由一开始样例有误,所以可能会有一部分人wa,但是只要老老实实按题目意思模拟,这个题很容易就A掉了。
H:SDUTOJ2498 数据结构实验之图论十一:AOE网上的关键路径
AC/SUBMIT : 0/3
考查知识点:最短路+拓扑序
大体就是SPFA(最短路贝尔曼算法的优化)逆向建图,然后通过入出度的关系找出原点和汇点,主要是将关键路径上的点输出,用一个pre数组记录,所求的最长路上的各个点。且值为上一节点,下标为下一节点。然后将pre数组所描述的路径,通过另一个数组
存下来,输出即可。
标程1:
标程2:
A:SDUTOJ4142 蚂蚁花呗
AC/SUBMIT : 25/245
by:郭小冉(徐红博)
考查知识点:栈
通过简单的栈模拟,对于向左走的蚂蚁如果没有向右走的蚂蚁直接加入进栈,如果有,则比较大小,向左的蚂蚁大吃掉向右走的蚂蚁(pop),否则是被吃掉。
对于向右走的直接加入栈中。
当然如果暴力的话,姿势好一点的话也可以过,但是由于测题时就有人暴力卡过,所以特意加了一组10000只蚂蚁全是左边的数据,因此卡掉了一部分暴力的做法。
比如下边的这种就正好被卡掉
#include <iostream> #include<stdio.h> #include<string.h> using namespace std; int s[100010],book[100010],b[100010]; int main() { int t,n,sum,i,j; cin>>t; while(t--) { cin>>n; sum=n; memset(book,0,sizeof(book)); for(i=0;i<n;i++) { cin>>s[i]>>b[i]; } for(i=0;i<n;i++) { if(b[i]==1) continue; for(j=i-1;j>=0;j--) { if(b[j]==0) continue; if(book[j]) continue; // printf("%d\n",j); if(s[i]>s[j]) { sum--; book[j]=1; } if(s[i]<s[j]) { book[i]=1; sum--; break; } } } printf("%d\n",sum); } return 0; }
标程1(c++版):
# include <bits/stdc++.h> using namespace std; int main() { int t; scanf("%d",&t); while(t--) { stack<int>s; int n, a, b, sum = 0; scanf("%d",&n); for(int i=0; i<n; ++i) { scanf("%d%d",&a,&b); if(b == 0) { while(!s.empty() && s.top() < a) ++sum, s.pop(); if(!s.empty()) ++sum; } else s.push(a); } printf("%d\n",n-sum); } return 0; }
标程2(c语言版):
#include<algorithm> #include<stdio.h> #include<string.h> #include<stdlib.h> using namespace std; int main() { int t; scanf("%d",&t); int a,b,n; while(t--) { int Stack[8124]; int top =0,sum =0 ; scanf("%d",&n); for(int i=1; i<=n; i++) { scanf("%d%d",&a,&b); if(!b) { while(top!=0&&Stack[top-1]<a) { ++sum; top--; } if(top!=0) { ++sum; } } else { Stack[top++] = a; } } printf("%d\n",n-sum); } return 0; }
B:SDUTOJ4143 蚂蚁森林
AC/SUBMIT : 0/10
by:郭小冉(徐红博)
考查知识点:排序+思维
比较考验思维的题目,应该属于这套题中思考难度最大的题目了;加了提示之后也没有人能想到(当然加完提示时剩下的时间已经比较短了)。
每只蚂蚁在木棍上都有自己的相对位置, 如果两只蚂蚁碰头就会改变自己的朝向,所以他们在木棍上的相对位置是不会改变的,给所有蚂蚁的位置标记大小并从小到大排序,设置初始数组和终态数组表示蚂蚁的状态,设置一个order数组表示蚂蚁被放置进去的顺序,在输出结果的时候根据这个顺序输出。
如果还不是很明白的话,可以看下面一组样例感受一下。
比如,有3只蚂蚁,蚂蚁1=(1, R),蚂蚁2= (3, L),蚂蚁3=(4, L),则两秒钟之后,3只蚂蚁分别为 1=(3,R)、2= (1,L)和 3= (2,L), 这是不转向的结果;
按照题意转向的话其结果应该为1=(1, L) , 2=(2, L), 3=( 3, R );即按位置排个序,就是它们对应的新位置;
关键点在于:对于蚂蚁来说, 可以把它们看成是没有区别的小点,那么只需独立计算出每只蚂蚁在T时刻的位置即可;
标程使用了STL中的排序,如果没有学过的同学可以先学习一下再尝试来解决这道题。
标程:
#include<stdlib.h> #include <algorithm> #include <stdio.h> #include<string.h> using namespace std; struct node { int x,dir,id; } cnt[10007],cnt_ans[10007]; int n,Time,l; const char name[][10]= {"L","Turning","R"}; int cmp(node a,node b) { return a.x<b.x; } int main() { int t,tt=0; scanf("%d",&t); while(t--) { tt++; printf("Case #%d\n",tt); scanf("%d%d%d",&l,&Time,&n); int t1,t2; char direction[5]; for(int i=1; i<=n; i++) { scanf("%d %s",&t1,direction); cnt[i].x = t1; cnt[i].id = i; if(direction[0]=='L')cnt[i].dir = -1; else cnt[i].dir = 1; cnt_ans[i].x = t1+Time*cnt[i].dir; cnt_ans[i].dir = cnt[i].dir; cnt_ans[i].id = i; } sort(cnt+1,cnt+1+n,cmp); int id_change[10007]= {0}; for(int i=1; i<=n; i++) { id_change[cnt[i].id] = i; } sort(cnt_ans+1,cnt_ans+1+n,cmp); for(int i=1; i<=n-1; i++) { if(cnt_ans[i].x==cnt_ans[i+1].x) { cnt_ans[i].dir = cnt_ans[i+1].dir = 0; } } for(int i=1; i<=n; i++) { int yy = id_change[i]; if(cnt_ans[yy].x<0||cnt_ans[yy].x>l) printf("Fell off\n"); else printf("%d %s\n",cnt_ans[yy].x,name[cnt_ans[yy].dir+1]); } printf("\n"); } }
C:SDUTOJ4144 蚂蚁财富
AC/SUBMIT : 3/8
by:郭小冉(徐红博)
考查知识点:思维+模拟
本题的代码不是十分的复杂,但是就思考上来说也有一定的难度,所以在一开始就发了公告提示。
有些人可能觉得可以暴搜,因为每只蚂蚁只有两种情况,不过每只蚂蚁都要掉头是不是很麻烦,而且时间复杂度为2的n次幂,肯定超时。 因为所有蚂蚁是同时出发的,相遇时的两只蚂蚁用的时间是相同的,我们可以无视蚂蚁的区别,当两只蚂蚁相遇时它们保持原样交错而行。这样每只蚂蚁都是独立运动的,那么只要找每只蚂蚁掉下去的时间就行了。
标程:
#include<cstdio> #define maxn 1000100 int a[maxn],ans,L,n; int MIN(int a,int b) { return a<b?a:b; } int MAX(int a,int b) { return a>b?a:b; } void ansMIN() { int i,min; ans=-1; for(i=0; i<n; ++i) { min=MIN(a[i],L-a[i]); if(min>ans) ans=min; } printf("%d ",ans); } void ansMAX() { int i,max; ans=-1; for(i=0; i<n; ++i) { max=MAX(a[i],L-a[i]); if(max>ans) ans=max; } printf("%d\n",ans); } int main() { int i,t; scanf("%d",&t); while(t--) { scanf("%d%d",&L,&n); for(i=0; i<n; ++i) scanf("%d",&a[i]); ansMIN();//求所有蚂蚁掉下去的最短时间 ansMAX();//求所有蚂蚁掉下去的最长时间 } return 0; }
D:SDUTOJ4145 蚂蚁借呗
AC/SUBMIT : 18/53
by:郭小冉(徐红博)
考查知识点:dfs搜索|模拟
本题实际上是一个十分裸的搜索题, 但是由于题目要求,因此其实只用循环模拟就可以做,由于题目设计的时候下标从零开始,题目中给出这个条件的位置也不是十分的显眼,因此导致有些人可能在这个地方遇到了坑。但只要能够枚举出八种情况,这道题就能够轻松解决。
利用二维数组模拟方格,读取数据,初始化方格状态,蚂蚁位置等。按照题目思路进行模拟。
知道了蚂蚁在哪,朝向哪,将所在格子颜色反转(!mp[x][y]),下一步就是根据格子原来的颜色转向了,因为提供的方向是英文,如果用switch进行选择不免显得繁琐,所以可以用数字0123来代替上右下左四个方向,蚂蚁转向可以就用取余运算来进行,右转就是 (s+1)%4,左转就是(s+3)%4,然后根据方向移动改变坐标即可。
标程1(搜索版):
#include <iostream> #include <bits/stdc++.h> using namespace std; int xx[]={-1,0,1,0}; int yy[]={0,1,0,-1}; int mp[111][111]; void dfs(int x,int y,int pos,int step) { if(pos==0) { printf("%d %d\n",x,y); return ; } int t_step; if(mp[x][y]) t_step=(step+5)%4; else t_step=(step+3)%4; int fx=x+xx[t_step]; int fy=y+yy[t_step]; mp[x][y]=1-mp[x][y]; dfs(fx,fy,pos-1,t_step); } int main() { int t,i,j,n,m;; cin>>t; while(t--) { cin>>n>>m; for(i=0;i<n;i++) { for(j=0;j<m;j++) { scanf("%d",&mp[i][j]); } } int x,y,p,sp; char s[10]; scanf("%d%d%s%d",&x,&y,&s,&p); if(s[0]=='U') sp=0; if(s[0]=='D') sp=2; if(s[0]=='L') sp=3; if(s[0]=='R') sp=1; dfs(x,y,p,sp); } return 0; }
标程2(while循环模拟):
#include <cstdio> #include <cstring> #include <string> #include <algorithm> using namespace std; int m, n; int Graph[105][105]; int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; int main() { int t; scanf("%d",&t); while(t--) { scanf("%d %d", &m, &n); for(int i = 0; i < m; i++) for(int j = 0; j < n; j++) scanf("%d", &Graph[i][j]); int x, y, k; char s[5]; scanf("%d %d %s %d", &x, &y, s, &k); int to; //朝向 if(s[0] == 'U') to = 0; else if(s[0] == 'R') to= 1; else if(s[0] == 'D') to= 2; else if(s[0] == 'L') to= 3; while(k) { if(Graph[x][y] == 0) { if(to == 0) to = 3; else to--; } else { to = (to + 1) % 4; } Graph[x][y] = Graph[x][y]^1; x += dx[to]; y += dy[to]; k--; } printf("%d %d\n", x, y); } return 0; }
标程3(枚举八种情况):
#include <stdio.h> #include <string.h> int a[100][100]; int main() { int t; scanf("%d",&t); while(t--) { int m,n,i,j,x,y,k; char s; memset(a,0,sizeof(a)); scanf("%d %d",&m,&n); for(i=0;i<m;i++) for(j=0;j<n;j++) scanf("%d",&a[i][j]); scanf("%d %d %c %d",&x,&y,&s,&k); while(k--) { if(s=='U') { if(a[x][y]==1) { a[x][y]=0; s='R'; y++; } else if(a[x][y]==0) { a[x][y]=1; s='L'; y--; } } else if(s=='D') { if(a[x][y]==1) { a[x][y]=0; s='L'; y--; } else if(a[x][y]==0) { a[x][y]=1; s='R'; y++; } } else if(s=='L') { if(a[x][y]==1) { a[x][y]=0; s='U'; x--; } else if(a[x][y]==0) { a[x][y]=1; s='D'; x++; } } else if(s=='R') { if(a[x][y]==1) { a[x][y]=0; s='D'; x++; } else if(a[x][y]==0) { a[x][y]=1; s='U'; x--; } } } printf("%d %d\n",x,y); } return 0; }
E:SDUTOJ4051 巨斧砍大树
AC/SUBMIT : 13/33
by:bLue(刘洋)
考查知识点:树
建立二叉树,然后在删除子树时只要将该子树的根节点置为 NULL 即可
标程:
// by bLue #include <cstdio> using namespace std; struct node { int data; node *left; node *right; }; bool is_first; node * Build(node *p, int data) { if(!p) { p = new node; p->data = data; p->left = p->right = NULL; } else { if(data < p->data) p->left = Build(p->left, data); else p->right = Build(p->right, data); } return p; } node * Delete(node *p, int data) { if(!p) printf("Already cut %d\n", data); else { if(data == p->data) { printf("Cut %d\n", data); p = NULL; } else if(data < p->data) p->left = Delete(p->left, data); else p->right = Delete(p->right, data); } return p; } void InorderTraverse(node *p) { if(p) { InorderTraverse(p->left); if(is_first) is_first = false; else printf(" "); printf("%d", p->data); InorderTraverse(p->right); } } int main(int argc, char const *argv[]) { int n, m, v; node *root = NULL; scanf("%d %d", &n, &m); for(int i=0; i<n; ++i) { scanf("%d", &v); root = Build(root, v); } for(int i=0; i<m; ++i) { scanf("%d", &v); root = Delete(root, v); is_first = true; InorderTraverse(root); printf("\n"); } return 0; }
F:SDUTOJ4146 龙王的工作
AC/SUBMIT : 30/67
by:玄黄(秦圣昭)
考查知识点:思维
实际上,这道题目是作为最简单的签到题的,只要读懂题意,那么这道题目就能够A掉。注意步兵时不移动的。由于玉将每次只能行进一格,那么要想回合数最少肯定是每一步都往步兵行进的方向去走,而但二者差别较远时肯定是斜着走最优,当二者走到同一行或者同一列时,玉将就只能直着走了,也就是说答案其实是两者横纵坐标相减,差别最大的那一个。
code:
#include <stdio.h> #include <stdlib.h> #include<math.h> int max(int a,int b) { if(a>b) return a; else return b; } int main() { int x1,y1,x,y,max1; while(scanf("%d %d %d %d",&x1,&y1,&x,&y)!=EOF) { max1=max(abs(x1-x),abs(y1-y)); printf("%d\n",max1); } return 0; }
G:SDUTOJ4141 唱歌的小P
AC/SUBMIT : 92/404
by:IceCapriccio(王炜良)
考查知识点:模拟
由一开始样例有误,所以可能会有一部分人wa,但是只要老老实实按题目意思模拟,这个题很容易就A掉了。
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char a[1006]; while(gets(a)) { //getchar(); int i; for(i=0;a[i]!='\0';i++) { if(a[i]=='B') { printf("D"); } else if(a[i]=='D') { printf("B"); } else if(a[i]=='P') { printf("B"); } else if(a[i]=='Z') { printf("\n"); } else if(a[i]==' ') ; else { printf("%c", a[i]); } } printf("\n"); } return 0; }
H:SDUTOJ2498 数据结构实验之图论十一:AOE网上的关键路径
AC/SUBMIT : 0/3
考查知识点:最短路+拓扑序
大体就是SPFA(最短路贝尔曼算法的优化)逆向建图,然后通过入出度的关系找出原点和汇点,主要是将关键路径上的点输出,用一个pre数组记录,所求的最长路上的各个点。且值为上一节点,下标为下一节点。然后将pre数组所描述的路径,通过另一个数组
存下来,输出即可。
标程1:
#include<bits/stdc++.h> using namespace std; struct edge{ int v,w,pre; }p[55555]; int n,m,next[10086],head[10086],cnt,vis[10086],dis[10086],i,u,v,w; int main() { while(~scanf("%d%d",&n,&m)) { memset(head,-1,sizeof(head)); memset(next,0,sizeof(next)); memset(vis,0,sizeof(vis)); memset(dis,0,sizeof(dis)); cnt=0,vis =1; for(i=0;i<m;i++) { scanf("%d%d%d",&v,&u,&w); p[cnt].v=v,p[cnt].w=w,p[cnt].pre=head[u],head[u]=cnt++; } queue<int>q; q.push(n); while(!q.empty()) { u=q.front(); q.pop(); vis[u]=0; for(i=head[u];~i;i=p[i].pre) if(dis[p[i].v]<dis[u]+p[i].w||(dis[p[i].v]==dis[u]+p[i].w&&next[p[i].v]>u)) { dis[p[i].v]=dis[u]+p[i].w,next[p[i].v]=u; if(!vis[p[i].v]) { q.push(p[i].v); vis[p[i].v]=1; } } } printf("%d\n",dis[1]); for(i=1;next[i];i=next[i]) printf("%d %d\n",i,next[i]); } return 0; }
标程2:
#include <bits/stdc++.h> using namespace std; const int Inf = 0x3f3f3f3f; struct node { int v; int w; int next; }edge[50004]; queue <int> que; int cnt, head[10004], dis[10004], vis[10004]; int pre[10004], in[10004], out[10004]; int num, ans[10004]; void Add(int u, int v, int w); void spfa(int s, int e, int n); int main(){ int n, m, i, u, v, w; while(scanf("%d %d", &n, &m) != EOF){ cnt = 0; memset(head, -1, sizeof(head)); memset(in, 0, sizeof(in)); memset(out, 0, sizeof(out)); while(m--){ scanf("%d %d %d", &u, &v, &w); Add(v, u, w); in[u]++, out[v]++; } for(i = 1; i <= n; i++){ if(!in[i]) u = i; if(!out[i]) v = i; } spfa(u, v, n); } return 0; } void Add(int u, int v, int w){ edge[cnt].v = v; edge[cnt].w = w; edge[cnt].next = head[u]; head[u] = cnt++; } void spfa(int s, int e, int n){ int i, u, v; memset(dis, -Inf, sizeof(dis)); memset(pre, Inf, sizeof(pre)); memset(vis, 0, sizeof(vis)); while(!que.empty()){ que.pop(); } que.push(s); dis[s] = 0; vis[s] = 1; while(!que.empty()){ u = que.front(); que.pop(); vis[u] = 0; for(i = head[u]; ~i; i = edge[i].next){ v = edge[i].v; if(dis[v] < dis[u] + edge[i].w || (dis[v] == dis[u] + edge[i].w && u < pre[v])){ dis[v] = dis[u] + edge[i].w; pre[v] = u; if(!vis[v]){ vis[v] = 1; que.push(v); } } } } printf("%d\n", dis[e]); num = 0; for(i = e; i != Inf; i = pre[i]) ans[num++] = i; for(i = 1; i < num; i++) printf("%d %d\n", ans[i-1], ans[i]); }
相关文章推荐
- 山东理工大学 2017级寒假ACM集训阶段测试赛题解
- 2015暑假ACM集训结训赛(第一场)(SDUTOJ)
- 2013寒假ACM集训_最短路
- 2014寒假ACM集训13级PK赛-翻转排序
- 2014寒假ACM集训13级PK赛-购买矿石
- 2014寒假ACM集训13级PK赛-最佳拟合直线
- ACMer菜鸟的ACM寒假集训感悟
- ACM寒假集训部分题目总结
- (寒假集训)Roadblock(最短路)
- ACM-ICPC集训--数学基础知识
- 寒假集训 day6
- 2015年14级寒假集训--素数筛
- 寒假集训 Day2 D CodeForces - 825B:五子棋
- SDUT - 2017年寒假集训 阶段测试赛3(个人) -- 解题报告
- ACM集训日记-8月16日
- 暑假ACM集训报告总结
- 烟台大学acm暑期集训总结
- 寒假集训附加题目题解报告(1)——数列
- 寒假的ACM训练(一)
- (寒假集训) 不等数列