今天果然是忙碌的一天,一大早起来开始刷题,开始还感觉不是那么难,但到后面,各种推导和模拟也真是让人头大。好多题目也是看了题解才做出来的,但愿随着不断积累,眼界不断开阔最后能由量变引起质变吧。。。
一、先总结一个较简单的题目:
接水问题
题目描述
有n个人在一个水龙头前排队接水,假如每个人接水的时间为Ti,请编程找出这n个人排队的一种顺序,使得n个人的平均等待时间最小。
输入输出格式
输入格式:
输入文件共两行,第一行为n;第二行分别表示第1个人到第n个人每人的接水时间T1,T2,…,Tn,每个数据之间有1个空格。
输出格式:
输出文件有两行,第一行为一种排队顺序,即1到n的一种排列;第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。
输入输出样例
说明
n<=1000
ti<=1e6,不保证ti不重复
当ti重复时,按照输入顺序即可(sort是可以的)
。。。。。。。。。。。。。。。。。。。。。。分割线 。。。。。。。。。。。。。。。。。。。。。。。。。。。
该题主要题意是说n个同学在1个(1个水龙头使问题更简单了)水龙头面前接水,已知他们每个人的接水时间,求经过排队后使所有同学的平均等待时间最小的时间。
一开始一直没弄懂这么平均等待时间是什么意思,所以导致不知道怎么处理。平均等待时间即分别把每个同学自第一个同学开始接水到自己开始接水(不是接完水)的等待时间相加,最后 /n,得到相应的值。(注意区分课本例题中的 花费的总时间(包含每个同学自己打水用的时间))。
弄清这个问题之后就很容易了,用贪心只要保证从第一步开始的以后每一步均取最小的那个时间就OK。
所以实现代码:
#include<bits/stdc++.h>
using namespace std;
struct student
{
int t,index;
}a[1001];
bool complare(student x,student y)
{
if(x.t==y.t)return x.index<y.index;
return x.t<y.t;
}
int main()
{
int n;
double time=0;
cin>>n;
for(int i=1;i<=n;++i)cin>>a[i].t,a[i].index=i;
sort(a+1,a+1+n,complare);
for(int i=1;i<=n;++i)
{
time+=a[i].t*(n-i);
}
time/=n;
for(int i=1;i<=n;++i)cout<<a[i].index<<" ";
cout<<endl;
printf("%.2lf",time);
return 0;
}
此外,鉴于后面有几个题都涉及到了数学分析的思想,所以在这里对为什么把用时少的同学放在前面会使总时间最少做下证 明:
/* 现在已知接水用时t(a)<t(b),当排队顺序为 a,b 时,总等待时间 t1=t(a),即b同学等了t(a)秒;
当排队顺序为b,a 时,总等待时间 t2=t(b),即后面的a同学等了t(b)秒;
显然得t1<t2,因此反推回去,若t1<t2,则令用时少的t(a)在前面。*/
二、纪念品分组
中间有个纪念品分组的题,大体题意是说 对一堆不同价值的礼物分组,最多2个一组,且保证每组的价值和均小于某一特定值,考虑怎样才能使分组最少,并输出 组数。
该题只要分别定义一个头指针和尾指针就好了,关键想说这个遍历并比较的过程其实就是快排的扫描方法,所以应熟记这段代码,(尤其注意等号是否可取的问题),具体。。。看课本吧。
。。。。。。。。。。。。。。。。。。。。。。分割线。。。。。。。。。。。。。。。。。。。。。。。。。。
三、凌乱的yyy
题目描述
现在各大oj上有n个比赛,每个比赛的开始、结束的时间点是知道的。
yyy认为,参加越多的比赛,noip就能考的越好(假的)
所以,他想知道他最多能参加几个比赛。
由于yyy是蒟蒻,如果要参加一个比赛必须善始善终,而且不能同时参加2个及以上的比赛。
输入输出格式
输入格式:
第一行是一个整数n ,接下来n行每行是2个正整数ai,bi(ai<bi),表示比赛开始、结束的时间。
输出格式:
一个整数最多参加的比赛数目。
输入输出样例
说明
对于20%的数据,n≤10;
对于50%的数据,n≤1000;
对于70%的数据,n≤100000;
对于100%的数据,n≤1000000,0≤ai<bi≤1000000。
。。。。。。。。。。。。。。。。。。。。。。题目与总节分割线。。。。。。。。。。。。。。。。。。。。。。。。。该题目算是一个题型吧,关键在于是对结束时间进行排序。
题解中看到了一个较好的解释:
//在一个数轴上有n条线段,现要选取其中k条线段使得这k条线段两两没有重合部分,问最大的k为多少。
//最左边的线段放什么最好?
//显然放右端点最靠左的线段最好,从左向右放,右端点越小妨碍越少
//其他线段放置按右端点排序,贪心放置线段,即能放就放
代码实现就比较简单了,关键还是对这种模型的理解。
。。。。。。。。。。。。。。。。。。。。分割线。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
四、P1031 均分纸牌
地址:https://www.luogu.org/problemnew/show/P1031
总结本题的原因主要是想掌握其中的去0操作,虽然理解起来很简单,但是每次实现的时候总有那么一点小细节搞错。
int p=1,q=n;
while(a[p]==0&&p<=n)p++;
while(a[q]==0&&q>=1)q--;//p,q的停留位置是第一个非0位置
同时,本题提醒自己对用while和for做扫描的操作很不熟悉,常常在一些细节问题上犹豫,比如指针的最终停留位置及p<q还是p<=q的问题。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。分割线。。。。。。。。。。。。。。。。。。。。。。。。。
五、P1080 国王游戏(NOIP 2012 提高组 第一天 第二题)
题目描述
恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。
国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。
输入输出格式
输入格式:
第一行包含一个整数 n,表示大臣的人数。
第二行包含两个整数 a和 b,之间用一个空格隔开,分别表示国王左手和右手上的整数。
接下来 n 行,每行包含两个整数 a 和 b,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。
输出格式:
输出只有一行,包含一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。
输入输出样例
说明
【输入输出样例说明】
按 1、2、3 号大臣这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;
按 1、3、2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;
按 2、1、3 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;
按 2、3、1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 9;
按 3、1、2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;
按 3、2、1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 9。
因此,奖赏最多的大臣最少获得 2 个金币,答案输出 2。
【数据范围】
对于 20%的数据,有 1≤ n≤ 10,0 < a、b < 8;
对于 40%的数据,有 1≤ n≤20,0 < a、b < 8;
对于 60%的数据,有 1≤ n≤100;
对于 60%的数据,保证答案不超过 10^9;
对于 100%的数据,有 1 ≤ n ≤1,000,0 < a、b < 10000。
。。。。。。。。。。。。。。。。。。。。。。。。。分割线。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
该类题目很明显我们要找一种排序方式,这也是本题的难点所在。联想到前面提到的接水问题,还有后面总结中会提到的皇后奖励问题,我们发现,对于该类题目,一般是拿出相邻的两组列出几种排列方式(也就两种,一前一后),然后写出所有可能解,再假设我们需要的值在最后取得(或前面),进而反推出 应该选择哪种排列方式,即排列规则。
。。。。。。。。。。。。。。。。。。。。。。。。。分割线。。。。。。。。。。。。。。。。。。。。。。。。。。。。
别人的题解分析:https://www.luogu.org/problemnew/solution/P1080
我们对于国王身后的两个点来分析
队列可能是这样的:
* | Left | Right |
---|---|---|
king: | a0 | b0 |
p1 | a1 | b1 |
p2 | a2 | b2 |
那么我们计算可得ans1 =max(b1a0,b2a0∗a1)
队列也有可能是这样的
* | Left | Right |
---|---|---|
king: | a0 | b0 |
p2 | a2 | b2 |
p1 | a1 | b1 |
那么我们计算可得ans2 =max(b2a0,b1a0∗a2)
我们来对比一下两个答案:
ans1 =max(b1a0,b2a0∗a1)
ans2 =max(b2a0,b1a0∗a2)
可以替换得:
ans1 =max(k1,k2)
ans2 =max(k3,k4)
显然我们可以得到:
b2a0∗a1 >b2a0
b1a0∗a2 >b1a0
即: k2 >k3
k4 >k1
如果ans1 <ans2
那么易得:
k4>k2
即: b1a0∗a2 >b2a0∗a1
变形可得:
a1∗b1<a2∗b2
当a1∗b1<a2∗b2 时,我们也能够得到ans1 <ans2 的结论
所以,为了ans 取到最小值,我们需要将ai∗bi 较小的放在前面
那么我们以ai∗bi 为关键字排序即可。
。。。。。。。。。。。。。。。。。。。。。分割线。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
另外,此题因为用到高精度,对这部分知识先不做深究,附上60分代码(第二个点有问题,加了特判,输出0时改为输出1)。
#include<bits/stdc++.h>
using namespace std;
struct member
{
int r,l,w;
}a[1001];
bool complare(member x,member y)
{
return x.w<y.w;
}
int main()
{
int n;cin>>n;
int x,y;
cin>>x>>y;
for(int i=1;i<=n;++i)cin>>a[i].l>>a[i].r,a[i].w=a[i].l*a[i].r;
sort(a+1,a+1+n,complare);
int sum=x;
for(int i=1;i<=n-1;++i)sum*=a[i].l;
int ans=sum/(a[n].r);
if(ans==0)ans=1;
cout<<ans;
return 0;
}
最后:可以看出,此类题目的代码实现并不难,关键问题是找出排序条件。
晚安