2017级寒假ACM集训结训赛--官方题解

时间:2021-11-09 12:49:16

博客地址

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[n]=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]);
}