本周主要学习贪心算法和模拟算法
1、
题目:突然,你收到从指挥部发来的信息,敌军的轰炸机正朝着你所在的独木桥飞来!为了安全,你的部队必须撤下独木桥。独木桥的长度为 L,士兵们只能呆在坐标为整数的地方。所有士兵的速度都为 1,但一个士兵某一时刻来到了坐标为 0或 L+1 的位置,他就离开了独木桥。每个士兵都有一个初始面对的方向,他们会以匀速朝着这个方向行走,中途不会自己改变方向。但是,如果两个士兵面对面相遇,他们无法彼此通过对方,于是就分别转身,继续行走。转身不需要任何的时间。由于先前的愤怒,你已不能控制你的士兵。甚至,你连每个士兵初始面对的方向都不知道。因此,你想要知道你的部队最少需要多少时间就可能全部撤离独木桥。另外,总部也在安排阻拦敌人的进攻,因此你还需要知道你的部队最多需要多少时间才能全部撤离独木桥。
思路:最少时间就是最中间那一个或者两个士兵以最快时间走到桥下的时间(可能从左边走下去也可能从右边走下去)
要求解最长时间:先把士兵面对面相遇看做是这两个士兵交换身体(比如x士兵从2往右走,y士兵从5往左走桥长为10,他俩相遇可以直接看做x从2直接走到11(交换身体),y从5直接走到0(交换身体))。所以最长时间就是最靠近0的士兵走到了L+1的时间,或者最靠近L的士兵走到0的时间
# include <bits/stdc++.h>
using namespace std;
int main()
{
long long l=0,n=0,fast=0,slow=0;
scanf("%lld",&l);
scanf("%lld",&n);
long long num[n],dm[n],mm[n];
for(long long a=0;a<n;a++)
{
scanf("%lld",&num[a]);
}
for(long long b=0;b<n;b++)
{
if(num[b]>(l/2))
{
dm[b]=l+1-num[b];
mm[b]=num[b];
}
else{
dm[b]=num[b];
mm[b]=l+1-num[b];
}
}
std::sort(dm,dm+n);
std::sort(mm,mm+n);
fast=dm[n-1];
slow=mm[n-1];
if(n==0){
fast=slow=0;
}
printf("%d %d",fast,slow);
return 0;
}
2、
题目:有 n 个人在一个水龙头前排队接水,假如每个人接水的时间为 Ti,请编程找出这 n 个人排队的一种顺序,使得 n 个人的平均等待时间最小。第一行为一个整数 n。第二行 n 个整数,第 i 个整数 Ti 表示第 i 个人的等待时间 Ti。输出文件有两行,第一行为一种平均时间最短的排队顺序;第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。
思路:时间短的先排前面(排序),只要把排序之前的位置储存起来,之后再输出即可,第一个人的等待时间就是T1*(n-1),所有人等待时间之和除以n就是平均等待时间
如何储存排序之前的位置? 可以用这种办法:由于数据Ti,n都是在0到1000之间,所以让Ti都变成1111*Ti+n(n为排序前的位置),这样之后,Ti/1111即为原来的Ti(不会改变原来Ti的大小),Ti%1111即为排序前的位置。
# include <bits/stdc++.h>
using namespace std;
int main()
{
long long n;
double sum=0.00;
scanf("%lld",&n);
long long num[n];
for(long long b=0;b<n;b++)
{
scanf("%lld",&num[b]);
num[b]=1111*num[b]+b;
}
std::sort(num,num+n);
for(long long d=0;d<n;d++)
{
sum+=num[d]/1111*(n-1-d);
printf("%d ",num[d]%1111+1);
}
printf("\n%.2lf",sum/n);
return 0;
}
3、
题目:
现在各大 oj 上有 n 个比赛,每个比赛的开始、结束的时间点是知道的。yyy 认为,参加越多的比赛,noip 就能考的越好(假的)。所以,他想知道他最多能参加几个比赛。由于 yyy 是蒟蒻,如果要参加一个比赛必须善始善终,而且不能同时参加 2 个及以上的比赛。输入:第一行是一个整数 n,接下来 n 行每行是 2 个整数 ai,bi (ai<bi),表示比赛开始、结束的时间。输出一个整数最多参加的比赛数目。
思路:越早结束可选的机会就越多。由于要尽可能地早点结束,因此要把结束时间排序,由于一个开始时间对应一个结束时间(即a1对应b1,a2对应b2,ai对应bi)因此要把排序前的顺序(1,2,i)储存下来,用第二题的储存方法,然后比较(排序后的最早的比赛结束时间)和(“”第二早的比赛结束时间“”’所对应的比赛开始时间),如果前者(第一结束时间)>=后者(第二结束时间对应的开始时间)那么可以同时参加这ans++,如果不行就再找下一个,如果行就再从下一个开始找,直到找遍所有比赛
# include <bits/stdc++.h>
using namespace std;
int main()
{
long long n=0,ans=1,c=1;
scanf("%lld",&n); //只要不冲突,越早结束越好
long long num[n],dm[n];
for(long long a=0;a<n;a++)
{
scanf("%lld %lld",&num[a],&dm[a]);
dm[a]=1000001*dm[a]+a;
}
std::sort(dm,dm+n);
for(long long b=0;b<n;)
{
while(dm[b]/1000001>num[dm[c]%1000001])
{
c++;
if(c>=n)
{
ans--;
break;
}
}
ans++;
b=c;
c=b+1;
}
printf("%lld\n",ans);
return 0;
}
4、
题目:
有 N 堆纸牌,编号分别为 1,2,…,N。每堆上有若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若干张纸牌,然后移动。移牌规则为:在编号为 11 堆上取的纸牌,只能移到编号为 22 的堆上;在编号为 N 的堆上取的纸牌,只能移到编号为 N−1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。
例如 N=4 时,4 堆纸牌数分别为 9,8,17,6。移动 33 次可达到目的:
从第三堆取 44 张牌放到第四堆,此时每堆纸牌数分别为 9,8,13,10。
从第三堆取 33 张牌放到第二堆,此时每堆纸牌数分别为 9,11,10,10。
从第二堆取 11 张牌放到第一堆,此时每堆纸牌数分别为 10,10,10,10。
思路:先求出所有纸牌的和sum,算出每一堆应该放的纸牌的个数average,然后将每一堆牌数都减去average(若average为10,那么9就会变成-1),把每一堆的牌(牌数减去average,-1...)往右移,如果当前的一堆为0,就ans不变,牌堆也不右移,如果不为0(为1、-1什么的),就往右移,ans++,最后输出ans
# include <bits/stdc++.h>
using namespace std;
int main()
{
long long n,sum=0,ave,ans=0;
scanf("%lld",&n);
long long num[n];
for(long long a=0;a<n;a++)
{
scanf("%lld",&num[a]);
sum+=num[a];
}
ave=sum/n;
for(long long m=0;m<n;m++)
{
num[m]-=ave;
}
for(long long b=0;b<n;b++)
{
if(num[b]!=0)
{
num[b+1]+=num[b];
ans++;
}
}
printf("%lld",ans);
return 0;
}