排序3
T1 拯救花园
题目:
一天,晨晨发现自己的 n(2≤n≤100)只兔子跑到自己的花园里面,它们在尽情的吃 着她的宝贝花卉。晨晨看在眼里痛在心里,她现在只能把兔子逐个的抓回笼子里面。而送 每只兔子回去的时间都不同,例如送第 i 只兔子回去需要 ti(1≤ti≤100)单位时间,那么 晨晨送第 i 只兔子来回共需要花费 2*ti 单位时间,另外每一只兔子单位时间的破坏力都不同,例如第 i 只兔子单位时间内破坏 di (1≤di≤100)朵花。
现在的问题是,晨晨如何安排送这 n 只兔子回笼子才能使这些兔子的破坏最小。
输入
第一行:一个整数 n(1≤n≤100);
接着有 n 行,每行两个空格分开的整数 ti、di,分别代表第 i 只兔子的送回去的时间, 和单位时间破坏力。
输出
一个整数,代表这些兔子破坏多少花卉。
输入样例
6
3 1
2 5
2 3
3 2
4 1
1 6
输出样例
86
思路:
一开始看到这个题,其实我的思路和大部分人都一样,就是优先送走破坏力大的兔子,但是按照这个思路写就会发现这样是行不通的,因为认真分析一下就会发现,当我们手上有n只兔子时(这里暂时当做两只),一只兔子的破坏力为10,运送时间为10分钟,而另一只兔子破坏力为5,运送时间为2分钟,这时候可以发现,当我先送破坏力高的兔子时,造成的破坏是5 * 10 * 2=100,;而当我先运第二只兔子时,造成的破坏是10 * 2 * 2=40。由此可见,实际上运送兔子的顺序不是兔子破坏力的大小,而是其他兔子造成的破坏大小。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,h,k;
struct tz
{
int ph,sj,hj;
} a[110];
bool cmp(tz q,tz p)
{
return p.ph*q.sj<q.ph*p.sj;//核心排序思想
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i].sj>>a[i].ph;
for(int i=1;i<=n;i++)
k+=a[i].ph;//存兔子的总破坏力
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)
{
k-=a[i].ph;
h+=k*a[i].sj*2;
}
cout<<h;
return 0;
}
T2 公路选址
题目:
在一条笔直的公路上种有 n 棵树,已知它们距离马路一端的距离。
现在需要在马路上选择一个浇水点,使得这个浇水点到每棵树的距离之和最小。
请你计算这个最小值。
输入
第一行一个正整数n,表示树的棵数。1≤n≤100000
第二行n个整数,表示每棵树距离一端的位置。(不大于10^8)
输出
一个整数,表示距离之和的最小值。
输入样例
4
6 2 9 1
输出样例
12
思路:
看到这道题目,很多人第一时间想到的就是在最远的两个点中点设置浇水点,但这个其实并不正确,在绝对值当中有一种问题叫最值问题,即给出几个包含未知数的绝对值式子,
例如:|x-1|+|x-2|+|x-3|,求该式的最小值,这类题就和拯救花园有着同样思想,即中位数,准确的说就是当只有奇数个点的时候,x=中点;当有偶数(n)个点的时候,
第n/2个点<=x<=第n/2+1个点,也就是奇中点偶中段。而我们可以明确地看到,x的值无论有几个点,都可以是第n/2+1个点上,所以只要求出了x的位置,接下来就好做多了。
PS:一定要用long long!!! int(不够大)和unsigned(会输出一些奇怪的东西,我样例那里就输出了3,换成 long long就没问题)都不能用。
代码:
#include<bits/stdc++.h>
using namespace std;
long long n,h,k,a[100010];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
sort(a+1,a+1+n);
k=a[n/2+1];
for(int i=1;i<=n;i++)
h+=abs(k-a[i]);
cout<<h;
return 0;
}
T3 最近的一对
题目:
给出包含n个元素的数组a,求a中距离最近的一对 i,j,满足
i < j,a[i] = a[j]。如果同时存在多对,输出最小的 i 对应的a[i]。
例如:10个数
19,13,11,19,11,5,6,3,4,3
满足存在a[i]=a[j]的数字包括:19,11,3。
其中11,3这两对的距离更近,在距离相同的情况下,11的下标更靠前。
如果不存在相同的数字,输出 “No”
输入
第一行:1个数n表示数组的长度(2 <= n <= 100000)。
第二有n个用空格隔开的整数,对应数组的元素(1<= a[i] <= 10^9)
输出
符合条件的最小的a[i]。
输入样例
10
19
13
11
19
11
5
6
3
4
3
输出样例
11
思路:
我一开始的时候是直接每次输入就强行判断能否构成一对且距离最近,结果就一直卡住了,直接死循环了。然后我重新分析了一下题目,找到了另一种方法:先记录每次输入的数字的位置,然后sort一下,所有可以凑成一对的数字就会挨在一起,这时候我只要判断一下当前数字的旁边有没有和它相同的数字,如果有,再判断距离和位置,最后输出就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
long long n,h,k,s,lz1=200000,lz2=200000;
struct node
{
int ys,wz;
}a[100010];
bool cmp(node q,node p)
{
if(q.ys==p.ys)
return q.wz<p.wz;
return q.ys<p.ys;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].ys;
a[i].wz=i;
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<n;i++)
{
s=2000000;
if(a[i].ys==a[i+1].ys)
s=abs(a[i].wz-a[i+1].wz);
if(s<lz1)
{
lz1=s;
lz2=min(a[i].wz,a[i+1].wz);
k=a[i].ys;
}
else if(s==lz1&&min(a[i].wz,a[i+1].wz)<lz2)
{
lz1=s;
lz2=min(a[i].wz,a[i+1].wz);
k=a[i].ys;
}
}
if(k!=0)
cout<<k;
else
cout<<"No";
return 0;
}
T4 直播获奖(CSP/J 2020)
题目:
NOI2130 即将举行,为了增加观赏性,CCF 决定逐一评出每个选手的成绩,并直播即时的获奖分数线。本次竞赛的获奖率为 w%,即当前排名前 w% 的选手的最低成绩就是即时的分数线。
更具体地,若当前已评出了 p 个选手的成绩,则当前计划获奖人数为 max(1,⌊p×w%⌋),其中 w 是获奖百分比,⌊x⌋ 表示对 x 向下取整,max(x,y) 表示 x 和 y 中较大的数。如有选手成绩相同,则所有成绩并列的选手都能获奖,因此实际获奖人数可能比计划中多。
作为评测组的技术人员,请你帮 CCF 写一个直播程序。
输入
第 1 行两个正整数 n,w 。分别代表选手总数与获奖率。
第 2 行有 n 个非负整数,依次代表逐一评出的选手成绩。
输出
只有一行,包含 n 个非负整数,依次代表选手成绩逐一评出后,即时的获奖分数线。相邻两个整数间用一个空格分隔。
输入样例
10 60
200 300 400 500 600 600 0 300 200 100
输出样例
200 300 400 400 400 500 400 400 300 300
说明
测试点编号 n
1~3 =10
4~6 =500
7~10 =2000
11~17 =10000
18~20 =100000
对于所有测试点,每个选手的成绩均为不超过 600 的非负整数,获奖百分比 w 是一个正整数且 1≤w≤99。
在计算计划获奖人数时,如用浮点类型的变量(如 C/C++ 中的 float、double,Pascal 语言中的 real、double、extended 等)存储获奖比例为 w%,则计算 5×60% 时的结果可能为 3.000001 ,也可能为 2.999999 ,向下取整后的结果不确定。因此,建议仅使用整型变量,以计算出准确值。
思路:
这道题目我一开始的想法是当它每次输入一个新的分数时,我就sort一遍,再输出当前的分数线,但是因为数据达到了100000(10万),所以就TLE了,然后我看了很久题,发现了一些提示(上面加粗字体),成绩不超过600,这种数据,很明显就是让我们用桶排,但是因为把桶排解法想复杂了那么一点点和忽略了一点细节,我又没对,展示一下三次的代码。
第一次的代码:
cin>>k;
a[k]++;
x=i*w;
p=max(1,x);
for(int l=600;l>=1;l--)
{
for(int j=1;j<=a[l];j++)
b[++t]=l;
if(t>=p)
break;
}
cout<<b[p]<<" ";
第二次的代码:
cin>>k;
a[k]++;
x=i*w;
p=max(1,x);
for(int l=600;l>=1;l--)
{
t+=a[l];
if(t>=p)
{
cout<<l<<" ";
break;
}
}
代码:
第三次(正解)的代码:
cin>>k;
a[k]++;
x=i*w;
p=max(1,x);
for(int l=600;l>=0;l--)
{
t+=a[l];
if(t>=p)
{
cout<<l<<" ";
break;
}
}
结论:排序,其实包含的内容很广泛,但只要想到思路,用正确的排序方法(别一直想着sort),其实很容易就能做出来了,但是要注意以什么标准排序,避开惯性思维,认真分析排序逻辑,以防进入思维误区。