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

博客地址

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]);
}
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值