第一题
战争已经进入到紧要时间。你是运输小队长,正在率领运输部队向前线运送物资。运输任务像做题一样的无聊。你希望找些刺激,于是命令你的士兵们到前方的一座独木桥上欣赏风景,而你留在桥下欣赏士兵们。士兵们十分愤怒,因为这座独木桥十分狭窄,只能容纳 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的差值,加到下一堆上,同时移动次数加一