15周训练题

这篇博客包含了三道编程竞赛题目,涉及STL、队列、滑动窗口、数学思维和图论结构体排序等知识点。第一题讨论如何统计在一定时间内来自不同国家的乘客数量,使用队列模拟滑动窗口;第二题探讨如何在满足特定条件的情况下最大化道路数量,转化成求线段最大连接数的问题;第三题是关于游戏比赛的组织,通过构建有向图判断玩家能否赢得比赛。通过这些题目,读者可以加深对算法和图论的理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目: 海港https://ac.nowcoder.com/acm/problem/16439

题意:
第一行输入一个正整数n,表示小K统计了 n艘船的信息。
接下来n行,每行描述一艘船的信息:前两个整数ti和ki分别表示这艘船到达海港的时间和船上的乘客数量,接下来ki个整数x(i,j)表示船上乘客的国籍。
保证输入的ti是递增的,表示从小K第一次上班开始计时,这艘船在第ti秒到达海港。
计算出以每一艘船到达时间为止的24小时(24小时=86400秒)内所有乘船到达的乘客来自多少个不同的国家。

知识点:
STL、队列

题解:
用数组模拟队列来解决滑动窗口问题,题目中只需要按人数来储存国际信息。

代码:

#include<iostream>
using namespace std;
int s,i,n,t,k,r,w[100001],x[300002],y[300002];

int main(){
    cin>>n;
    while(n--){
        cin>>t>>k;
        while(k--){
            y[++r]=t;cin>>x[r];
            if(!w[x[r]])s++;
            w[x[r]]++;
        }
        while(t-y[i]>=86400)
            if(!--w[x[i++]])s--;
        cout<<s<<endl;
    }
	//system ("pause");
	return 0;
}

题目: https://ac.nowcoder.com/acm/problem/15973

题意:
一张地图上有有N个城市,他们可以通过双向道路互相连接,但是每两座城市只能有一条双向道路互相连接。

现在我们想要满足条件“地图中不能有任意三个城市可以互相直达”,请问满足这个条件的最大道路数是多少?

知识点:
数学,思维

题解:
通过分析可将本题等价转化为求两段点最多可以连接多少条线段。

代码:

//--ForBlue
//--2021-12-10 16:06:26
#include <bits/stdc++.h>
using namespace std;
 
typedef long long LL;
const int mod = 998244353;
 
int main() {
    int n;
    while (cin >> n)
    cout << (n / 2) * (n - n / 2) << endl;
    // system ("pause");
    return 0;
}

题目: https://codeforces.com/contest/1608/problem/C

题意:
N个玩家在玩游戏。
游戏中有两张不同的地图。对于每个玩家,我们知道他在每张地图上的力量。当两名玩家在特定地图上战斗时,在该地图上实力较高的玩家总是获胜。在同一张地图上没有两名玩家拥有相同的力量。
你是游戏大师,想组织一场比赛。总共会有n−1场战斗。当比赛中有一个以上的玩家时,你可以选择任意一张地图和任意两个剩余的玩家在上面战斗。输掉比赛的选手将被淘汰出局。
最后,只剩下一名选手,他被宣布为锦标赛的获胜者。每个选手都要决定自己是否能赢得比赛。

知识点:
图论,结构体排序

题解:
让我们以相反的顺序来看看这些打斗。如果参与人x是赢家,那么他在最后一场比赛中战胜了参与人y。在(n−2)第二次战斗中,x或y战胜了某个玩家z,以此类推。
我们总是通过增加一名至少可以输掉一场比赛的玩家来扩展集合,所以如果我们可以从x开始并以所有玩家的集合结束,x就可以赢得比赛。
如果我们构造一个有向图,即当且仅当u在战斗中战胜v时,从参与人u到参与人v添加一条边,那么问题就相当于找到一个节点集,从这个节点集我们可以到达所有其他节点
为了将边的数量减少到2n−2,我们可以按ai和bi降序对玩家进行排序,并按照这些顺序只添加从第i个玩家到(i+1)-th个玩家的边。

代码:

#include <bits/stdc++.h>
using namespace std;
 
const int N = 1e6 + 20;
 
 
int tt, n;
pair<int, int> a[N], b[N];
int ind[N], pat[N], ind1[N];
int mn[N], mn1[N];
void solve () {
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin>>a[i].first;
        a[i].second=i;
    }
    for (int i = 0; i < n; i++) {
        cin>>b[i].first;
        b[i].second=i;
    }
    sort(a,a+n);
    sort(b,b+n);
    for (int i = 0; i < n; i++)
        ind[b[i].second]=i, ind1[a[i].second]=i;
    
    int mx = 0;
    for (int i = n - 1; i >= 0; i--) {
        mn[i]=ind[a[i].second];
        mn1[i]=ind1[b[i].second];
        if(i!=n-1) mn[i]=min(mn[i], mn[i+1]), mn1[i]=min(mn1[i], mn1[i+1]);
    }
    int i1 = n-1, i2=n-1;
    while (i1!=mn[i2]||i2!=mn1[i1]) {
        i1=mn[i2];
        i2=mn1[i1];
    }
    for (int i = 0; i < n; i++) pat[i]=0;
    for (int i = 0; i < n; i++) {
        if(i>=i1) pat[b[i].second]=1;
        if(i>=i2) pat[a[i].second]=1;
    }
    for (int i = 0; i < n; i++) cout<<pat[i];
    cout<<endl;
}
int main() {
    cin >> tt;
    while (tt -- ) {
        solve ();
    }
    //system ("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Pale_B

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

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

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

打赏作者

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

抵扣说明:

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

余额充值