ACM算法题第二周

文章讲述了在战争中如何计算士兵撤离独木桥的最短和最长时间,以及如何通过优化队伍顺序减少接水等待时间。还涉及了比赛参与策略和调整纸牌数量的问题。

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

第一题

战争已经进入到紧要时间。你是运输小队长,正在率领运输部队向前线运送物资。运输任务像做题一样的无聊。你希望找些刺激,于是命令你的士兵们到前方的一座独木桥上欣赏风景,而你留在桥下欣赏士兵们。士兵们十分愤怒,因为这座独木桥十分狭窄,只能容纳 11 个人通过。假如有 22 个人相向而行在桥上相遇,那么他们 22 个人将无法绕过对方,只能有 11 个人回头下桥,让另一个人先通过。但是,可以有多个人同时呆在同一个位置。

题目描述

突然,你收到从指挥部发来的信息,敌军的轰炸机正朝着你所在的独木桥飞来!为了安全,你的部队必须撤下独木桥。独木桥的长度为 LL,士兵们只能呆在坐标为整数的地方。所有士兵的速度都为 11,但一个士兵某一时刻来到了坐标为 00 或 L+1L+1 的位置,他就离开了独木桥。

每个士兵都有一个初始面对的方向,他们会以匀速朝着这个方向行走,中途不会自己改变方向。但是,如果两个士兵面对面相遇,他们无法彼此通过对方,于是就分别转身,继续行走。转身不需要任何的时间。

由于先前的愤怒,你已不能控制你的士兵。甚至,你连每个士兵初始面对的方向都不知道。因此,你想要知道你的部队最少需要多少时间就可能全部撤离独木桥。另外,总部也在安排阻拦敌人的进攻,因此你还需要知道你的部队最多需要多少时间才能全部撤离独木桥。

代码:#include<bits/stdc++.h>
using namespace std;
int l,n,a[5005];
int main(){
    cin>>l>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    int max_time=0,min_time=0;
    if(n==0){
        cout<<"0 0";
        return 0;
    }
    sort(a+1,a+1+n);
    max_time=max(l-a[1]+1,a[n]);
    for(int i=1;i<=n;i++){
        min_time=max(min(l-a[i]+1,a[i]),min_time);
    }
    cout<<min_time<<' '<<max_time;
    return 0;

思路:题目中的两个士兵碰面后会反向,可以理解为两人将换位置后继续保持原方向行走,所以所花费的最长时间为最靠近端点的两人各自向对方的方向走,时间较长的那个人的时间;最短时间为所有人走完桥(到达终点或回到起点)的最短时间的最大值

第二题

有 n 个人在一个水龙头前排队接水,假如每个人接水的时间为 Ti,请编程找出这 n个人排队的一种顺序,使得 n个人的平均等待时间最小。

代码:#include<bits/stdc++.h>
using namespace std;
struct a{
    int a;
    int num;
}c[100005];
bool cmp(a x,a y){
    return x.num<y.num;
}
int n;
double ave,sum;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>c[i].num;
        c[i].a=i;
    }
    sort(c+1,c+1+n,cmp);
    for(int i=1;i<=n;i++){
        cout<<c[i].a<<' ';
        sum+=c[i].num*(n-i);
    }
    cout<<endl;
    ave=sum/n;
    printf("%.2lf",ave);
    return 0;
}

思路:要是平均等待时间最短,就是让打水时间长的尽量排在后面,在输入数据的时候可以构建一个结构体,分别存贮第i个人的打水时长以及她是第几个输入的,之后根据打水时长排序,在根据打水时长*(总人数-第几个打水)计算总时长

第三题:

现在各大 oj 上有 n 个比赛,每个比赛的开始、结束的时间点是知道的。

yyy 认为,参加越多的比赛,noip 就能考的越好(假的)。

所以,他想知道他最多能参加几个比赛。

由于 yyy 是蒟蒻,如果要参加一个比赛必须善始善终,而且不能同时参加 2 个及以上的比赛。

代码:

#include<bits/stdc++.h>
using namespace std;
int n;
struct a{
    int s,e;
}t[1000005];
bool cmp(a x,a y){
    return x.e<y.e;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>t[i].s>>t[i].e;
    }
    sort(t+1,t+1+n,cmp);
    int end=t[1].e,cnt=1;
    for(int i=2;i<=n;i++){
        if(t[i].s>=end){
            end=t[i].e;
            cnt++;
        }
    }
    cout<<cnt;
    return 0;

思路:为了参加尽可能多的比赛,先按照每场比赛的结束时间排序,然后判断前一场的结束时间和下一场的开始时间的关系,符合就场次加一

第四题:

有 N 堆纸牌,编号分别为 1,2,\ldots,N1,2,…,N。每堆上有若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若干张纸牌,然后移动。

移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 22 的堆上;在编号为 NN 的堆上取的纸牌,只能移到编号为 N-1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。

例如 N=4 时,4 堆纸牌数分别为 9,8,17,6。

移动 3 次可达到目的:

  • 从第三堆取 4 张牌放到第四堆,此时每堆纸牌数分别为 9,8,13,10。
  • 从第三堆取 3 张牌放到第二堆,此时每堆纸牌数分别为 9,11,10,10。
  • 从第二堆取 1 张牌放到第一堆,此时每堆纸牌数分别为 10,10,10,10。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,a[105],sum,ans,cnt;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        sum=sum+a[i];
    }
    ans=sum/n;
    for(int i=1;i<n;i++){
        if(a[i]==ans){
            continue;
        }else{
            cnt++;
            a[i+1]=a[i+1]+(a[i]-ans); 
        }
    }
    cout<<cnt;
    return 0;
}

思路:从最左边的一堆开始,当这一对满足N时,可以将这一堆移除,将下一堆当作第一堆,以此类推。最左边的一堆只能把牌移向下一堆,这里假设可以移动负数张牌,那么如果一开始这一堆的牌就等于N,不移动,直接移除;否则计算这一堆牌数和N的差值,加到下一堆上,同时移动次数加一

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值