武汉工程大学第三届ACM程序设计选拔赛

武汉工程大学第三届ACM程序设计选拔赛

A题(疯狂动物城)

题目链接:https://ac.nowcoder.com/acm/contest/19161/A

题目描述

动物城中有四类动物 A,B,C,D,这四类动物的食物链构成了有趣的环形。

A 吃 B,B 吃 C,C 吃 D,D 吃 A。

现有 N 个动物,以 1∼N 编号。

每个动物都是 A,B,C,D 中的一种,但是我们并不知道它到底是哪一种。

有人用三种说法对这 N 个动物所构成的食物链关系进行描述:

第一种说法是 1 X Y,表示 X 和 Y 是同类。

第二种说法是 2 X Y,表示 X 吃 Y。

第三种说法是 3 X Y,表示 X 和 Y 不是同类,且X 不吃 Y ,Y 也不吃 X。

此人对 N 个动物,用上述三种说法,一句接一句地说出 K 句话,这 K 句话有的是真的,有的是假的。

当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

当前的话与前面的某些真的话冲突,就是假话;
当前的话中 X 或 Y 比 N 大,就是假话;
当前的话表示 X 和 X不是同类,就是假话。
你的任务是根据给定的 N 和 K 句话,输出假话的总数。

题目分析

求出不满足题目中所给出的食物链中吃与被吃关系的说法条数

解题思路

带权值的并查集问题:
1.首先声明一个数组:下标数字表示吃的,数组中放的值表示被吃。
2.然后在声明一个数组代表每个关系的权值。
3.输入判断的值,找出两个数的根节点(需要更新关系的权值)
4.如果找到的两个值相等且权值之差为1并满足是查询吃与被吃关系的是真话,否则为假话。
5.如果找到的两个值不相等且满足查询条件3,则为真话,否则为假话。
6.统计假话的个数。

代码实现

#include<bits/stdc++.h>
using namespace std;
const int N=10000010;
int n,m,c,t,x,y,a[N],b[N];
int find(int x){
    if(b[x]!=x){
        int k=find(b[x]);
        a[x]=(a[x]+a[b[x]])%4;
        b[x]=k;
    }
    return b[x];
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)b[i]=i;
    while(m--){
        cin>>t>>x>>y;
        if(x>n||y>n||x<1||y<1||t!=1&&x==y) {c++;continue;}
        int p1=find(x),p2=find(y),k=t-1;
        if(p1==p2&&((a[x]-a[y])%4+4)%4!=k){c++;continue;}
        if(p1!=p2){
            b[p1]=p2;
            a[p1]=((k-a[x]+a[y])%4+4)%4;
        }
    }
    cout<<c<<endl;
    return 0;
}

B题(密室逃脱)

题目链接:https://ac.nowcoder.com/acm/contest/19161/B

题目描述

小明没有打过校赛但是他被拉去出题了。他非常想打校赛,但是他现在被困到了迷宫出题,现在请你帮小明逃离出题现场。该迷宫由n * m个格子组成的迷宫,有些格子是陷阱,用’#‘表示,小明进入陷阱就会死亡,’.'表示没有陷阱。小明所在的位置用’S’表示,目的地用’T’表示。

小明只能向上下左右相邻的格子移动,每移动一次花费1秒。

有q个双向传送阵,每个传送阵各有两个口,都在迷宫的格子里,当走到或被传送到一个有传送阵的格子时,小明可以选择是否开启传送阵。如果开启传送阵,小明就会被传送到传送阵的另一端的格子里,这个过程会花费3秒;如果不开启传送阵,将不会发生任何事情,小明可以继续向上下左右四个方向移动。

一个格子可能既有多个传送阵,小明可以选择任意一个开启传送阵。使用传送阵是非常危险的,因为有的传送阵的另一端在陷阱里,如果小明使用这样的传送阵,那他就会死亡。请告诉小明活着逃离出题现场的最短时间。

题目分析

从一点到另一点的最短路问题(传送带)。

解题思路

1.设置一个vector容器存放传送带的地址信息,找到起点和终点。
2.设置一个优先队列存放每走一步的最短路,数组dis更新最短路,数组vis标记走过的路。
3.先将dis数组中的值都置为最大值方便以后的更新。先开始时将起点先推入队列中,然后将步长和坐标取出,判断是否等于终点坐标,如果是则返回当前的步长,如果不是则寻找到下一个最短的步长更新再存到优先队列中,还要判断该点是否有传送门如果有且经过传送门的步长小于当前步长则继续更新存到优先队列中。依次循环往复最终找到终点返回步长,如果到达不了则返回-1。

代码实现

#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII;
typedef pair<int, PII> PIII;
const int N = 310;
const int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
int n, m, q;
int stx, sty, edx, edy;
char mp[N][N];
vector<PII> edges[N][N];
int dis[N][N];
bool vis[N][N];

int Dijkstra()
{
    memset(dis, 0x3f, sizeof dis);
    priority_queue<PIII, vector<PIII>, greater<PIII>> heap;
    heap.push({0, {stx, sty}});
    dis[stx][sty] = 0;
    while (!heap.empty()) {
        int distance = heap.top().first;
        int a = heap.top().second.first, b = heap.top().second.second;
        heap.pop();
        if (vis[a][b])
            continue;
        vis[a][b] = true;
        if (a == edx && b == edy) return distance;
        for (int i = 0; i < 4; i++) {
            int x = a + dx[i], y = b + dy[i];
            if (!(x >= 1 && x <= n && y >= 1 && y <= m && mp[x][y] != '#' && dis[x][y] > distance + 1))
                continue;
            dis[x][y] = distance + 1;
            heap.push({dis[x][y], {x, y}});
        }
        for (auto t : edges[a][b]) {
            int x = t.first, y = t.second;
            if (!(mp[x][y] != '#' && dis[x][y] > distance + 3))
                continue;
            dis[x][y] = distance + 3;
            heap.push({dis[x][y], {x, y}});
        }
    }
    return -1;
}
int main()
{
    cin >> n >> m >> q;
    for (int i = 1; i <= n; i++) {
        cin >> mp[i] + 1;
        for (int j = 1; j <= m; j++)
            if (mp[i][j] == 'S') stx = i, sty = j;
            else if (mp[i][j] == 'T') edx = i, edy = j;
    }
    while (q--) {
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        a++, b++, c++, d++;
        edges[a][b].push_back({c, d});
        edges[c][d].push_back({a, b});
    }
    cout << Dijkstra() << endl;
    return 0;
}

C题(露营?料理!)

题目链接:https://ac.nowcoder.com/acm/contest/19161/C

题目描述

S t a r r y S k y \mathit {StarrySky} StarrySky 的皮卡丘十分喜欢去露营,因为露营意味着玩耍,同时也意味着 S t a r r y S k y \mathit {StarrySky} StarrySky 要做料理给它吃。而 S t a r r y S k y \mathit {StarrySky} StarrySky 也明知这一点,于是,这一次的露营, S t a r r y S k y \mathit {StarrySky} StarrySky 一下子做了 n \mathit n n 份不同的料理,他将这 n \mathit n n 份料理摆成了一排,编号依次为 1 ,  2 , . . . , n \text 1,\ \text 2,...,\mathit n 1, 2,...,n,做完这些, S t a r r y S k y \mathit {StarrySky} StarrySky 突发奇想,想要出一个问题考考皮卡丘。

S t a r r y S k y \mathit {StarrySky} StarrySky 为每一个蛋糕规定了一个价值,分别用 w 1 , w 2 , . . . , w n \mathit w_\text 1,\mathit w_\text 2,...,\mathit w _\mathit n w1,w2,...,wn 表示,价值可能是负数,他会向皮卡丘提问 m \mathit m m 次,每一个询问给出一个值 k \mathit k k,而皮卡丘需要给出一个答案 p ( 0 ≤ p ≤ n ) \mathit p(\text 0 \leq \mathit p \leq \mathit n) p(0pn),使得 k − ∑ i = 0 p w i \mathit k-\sum \limits_{\mathit i=\text 0}^\mathit p\mathit w_\mathit i ki=0pwi 非负,但是结果最小,特殊的,如果有多个最小结果,那么,皮卡丘要给出最大的 p \mathit p p,而如果 p = 0 \mathit p=\text 0 p=0,则该表达式的值就为 k \mathit k k w 0 w_0 w0 的值恒为0。皮卡丘掰弄着手指,它多么想施放十万伏特电死 S t a r r y S k y \mathit {StarrySky} StarrySky,但是它不能,因为那样的话它以后就吃不到料理了,也不能一起战斗了,所以,皮卡丘向你求助,希望你能帮帮它。

题目分析

给定n个数和每个数的权值,在给出一个数k,求出最大的p使得 k   −   ∑ i   =  0 p w i \mathit k\ -\ \sum \limits_{\mathit i\ =\ \text 0}^\mathit p\mathit w_\mathit i k  i = 0pwi是一个非负数。

解题思路

首先创建一个结构体存放k值和p值,数组sum统计权值的前缀和。每次更改p的值实现表达式的值为非负,然后进行一个排序。取出最大的q。

代码实现

# include <iostream>
# include <cstdio>
# include <algorithm>
typedef long long ll;
const int N = 1e5 + 5;
int n;
struct Sum {
    ll ans;
    int id;
    const bool operator < (const Sum& rhs) const {
        return ans < rhs.ans || (ans == rhs.ans && id < rhs.id);
    }
};
ll w[N];
Sum sum[N];
int main() {
    int m;
    std::cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &w[i]);
    }
    for (int i = 1; i <= n; i++) {
        sum[i].ans = sum[i-1].ans + w[i];
        sum[i].id = i;
    }
    sum[n + 1].ans = 0;
    sum[n + 1].id = 0;
    std::sort(sum + 1, sum + 2 + n);
    while (m--) {
        int k;
        scanf("%d", &k);
        Sum t;
        t.ans = k;
        t.id = 0x3f3f3f3f;
        auto it = std::upper_bound(sum + 1, sum + 2 + n, t) - 1;

        if (it == sum) {
            puts("-1");
            continue;
        }

        printf("%d\n", it->id);
    }
    return 0;
}

E题(找规律)

题目链接:https://ac.nowcoder.com/acm/contest/19161/E

题目描述

将正整数按以下规律排列:

在这里插入图片描述

设 n 在第 i 行第 j 列,请输出 i 和 j 的最小公倍数。

题目分析

找到n所代表的下标i,j。求i和j的最小公倍数。

解题思路

首先观察,奇数行全都是奇数;偶数行全都是偶数。且奇数上是奇数的顺序(1,3,5,7···);偶数行上是偶数的顺序(2,4,6,8···)。所以首先应该找到n是第几行上的奇数或偶数,然后再通过行数找到对应的列数。之后再求最小公倍数。

代码实现

#include<bits/stdc++.h>
using namespace std;
int n,m,i,j;
int main(){
    cin>>n;
    m=(n+1)/2;//找到对应的行数
	if(n%2) i=1;else i=2;//判断n是属于奇数行还是偶数行
    while(m>i){m-=i;i+=2;} j=m;//找对应的列数
    cout<<i*j/__gcd(i,j)<<endl;
    return 0;
}

I题(我们是冠军)

题目链接:https://ac.nowcoder.com/acm/contest/19161/I

题目描述

小智和皮卡丘又一次站到了宝可梦对战的舞台上,这一次的赛制有所变化,采用的是四强双败赛制,换句话说就是:在四强之前,两两对战,败方直接淘汰,一旦进入四强,如果一场对局失利,就进入败者组,如果在败者组一路连胜,依旧可以晋级决赛,争夺冠军。

更直观一点,四强之后的比赛流程如下图所示:

在这里插入图片描述

如果已知一共有 n \mathit n n 位宝可梦训练家参赛,那么,小智和皮卡丘想要拿到冠军,最少需要比赛几场,最多需要比赛几场,以及最多场次和最少场次之间的差值是多少?

题目分析

采用四强双败赛制求出取得的冠军所用的最少场次和最大场次并求差值。

解题思路

首先如果一路都赢的话毋庸置疑是取得冠军所用的最少场次,如果输一局的话在败者组里全胜才能获得冠军。所以最多的取胜场次则是在进入四强时都是赢得,晋级四强后第一局就输,然后需要在败者组里全赢。在进入四强前最少和最多所用的场次都是一样的,唯一不同的是进入四强后在败者组多赢了一局。所以最多场次夺冠和最少场次夺冠的差值是一样的,就是在四强后多出来的那一局。

代码实现

#include<bits/stdc++.h>
using namespace std;
long long n,m,c;
int main(){
    scanf("%lld",&n);
    for(int i=0;i<n;i++){
        scanf("%lld",&m);
        c=0;
        while(m>4){c++;m/=2;}//四强前打了几局
        printf("%lld %lld 1\n",c+3,c+4);//总共打了多少局
    }
    return 0;
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Galactus_hao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值