您的位置:首页 > 其它

寒假训练记录一:排序问题

2018-01-30 13:04 246 查看
  感悟:刚开始学习排序,感觉这部分内容的基础知识较容易理解,没什么难懂的地方。不过随着这两天的刷题,还是学到了不少东西,发现有许多地方过去理解的还是过于浅显,希望自己在今后的学习中能对这些知识有更深刻的认识。

正文:

一、主要收获:(重点:结构体与sort、归并排序)

1、qsort:快排即通过一个中间值,将待排数列划分为大于该值和小于该值的两部分,然后再递归调用qsort对两边分别排序。

其主要缺点就是不稳定:比如说现有一已知序列3、6、1、5、6、2、7,在一次划分后得到3、2、1、5、6、6、7,最后排序得到的结果是1、2、3、5、6、6、7。然而这样就破坏了两个6的原有顺序,因此说它是不稳定的。

实现代码:

void qsort(int x,int y)
{
int p=x,q=y,mid;
mid=a[(x+y)/2];
do
{
while(a[p]<mid)p++;     //此处p有判断作用,所以不可写为a[p++]<mid,下行同理。
while(a[q]>mid)q--;
if(p<=q)
swap(a[p++],a[q--]);//无判断作用。
}while(p<=q);
if(p<y)qsort(p,y);
if(q>x)qsort(x,q);
}

2、归并排序:

msort的实现可分为三个部分,①划分问题 ②递归对两半元素排序 ③合并两个有序表

因此代码可写为:

void msort(int x,int y)
{
if(x==y)return ;
int mid=(x+y)/2;
msort(x,mid); //递归
msort(mid+1,y);
int p=x,q=mid+1,i=x;
while(p<=mid||q<=y) //合并操作
{
if(q>y||(p<=mid&&a[p]<=a[q]))r[i++]=a[p++];
else r[i++]=a[q++];
}
for(i=x;i<=y;++i)a[i]=r[i];
}
同时,我们也可以借助合并函数merge()来实现归并排序:
merge(a,b,c,d,e,f)

四个参数分别表示:

a:左序列首地址    b:左序列末位置    c:右序列首位置    d:右序列末位置    e:存放结果的数组首地址

f:排序规则complare

例题:


题目描述

2*N 名编号为 1~2N 的选手共进行R 轮比赛。每轮比赛开始前,以及所有比赛结束后,都会按照总分从高到低对选手进行一次排名。选手的总分为第一轮开始前的初始分数加上已参加过的所有比赛的得分和。总分相同的,约定编号较小的选手排名靠前。

每轮比赛的对阵安排与该轮比赛开始前的排名有关:第1 名和第2 名、第 3 名和第 4名、……、第2K – 1 名和第 2K名、…… 、第2N – 1 名和第2N名,各进行一场比赛。每场比赛胜者得1 分,负者得 0 分。也就是说除了首轮以外,其它轮比赛的安排均不能事先确定,而是要取决于选手在之前比赛中的表现。

现给定每个选手的初始分数及其实力值,试计算在R 轮比赛过后,排名第 Q 的选手编号是多少。我们假设选手的实力值两两不同,且每场比赛中实力值较高的总能获胜。


输入输出格式

输入格式:

输入文件名为swiss.in 。

输入的第一行是三个正整数N、R 、Q,每两个数之间用一个空格隔开,表示有 2*N 名选手、R 轮比赛,以及我们关心的名次 Q。

第二行是2*N 个非负整数s1, s2, …, s2N,每两个数之间用一个空格隔开,其中 si 表示编号为i 的选手的初始分数。 第三行是2*N 个正整数w1 , w2 , …, w2N,每两个数之间用一个空格隔开,其中 wi 表示编号为i 的选手的实力值。

输出格式:

输出文件名为swiss.out。

输出只有一行,包含一个整数,即R 轮比赛结束后,排名第 Q 的选手的编号。


输入输出样例

输入样例#1: 复制
2 4 2
7 6 6 7
10 5 20 15


输出样例#1: 复制
1



说明

【样例解释】



【数据范围】

对于30% 的数据,1 ≤ N ≤ 100;

对于50% 的数据,1 ≤ N ≤ 10,000 ;

对于100%的数据,1 ≤ N ≤ 100,000,1 ≤ R ≤ 50,1 ≤ Q ≤ 2N,0 ≤ s1, s2, …, s2N≤10^8,1 ≤w1, w2 , …, w2N≤ 10^8。

noip2011普及组第3题。
AC代码:
#include<bits/stdc++.h>
using namespace std;
struct node
{
int strength,f_score,index; //结构体在排序中常使用
}a[200001];
node b[100001],c[100001];

bool complare(node x,node y)
{
if(x.f_score==y.f_score)return x.index<y.index;
else return x.f_score>y.f_score;
}
int main()
{
int N,R,Q;
cin>>N>>R>>Q;
for(int i=1;i<=2*N;++i)cin>>a[i].f_score,a[i].index=i;
for(int i=1;i<=2*N;++i)cin>>a[i].strength;
sort(a+1,a+1+N*2,complare);
for(int i=1;i<=R;++i)
{
int p=1,q=1;
for(int j=1;j<=2*N;j+=2)
{
if(a[j].strength>a[j+1].strength)
{
a[j].f_score+=1;
b[p++]=a[j];
c[q++]=a[j+1];
}
else
{
a[j+1].f_score+=1;
c[q++]=a[j];
b[p++]=a[j+1];
}
}
merge(b+1,b+p,c+1,c+q,a+1,complare);

}
cout<<a[Q].index;
return 0;
}
3、sort与结构体结合。
此部分目前感觉较简单,直接上例题,及AC代码:


题目描述

一共有n(n≤20000)个人(以1--n编号)向佳佳要照片,而佳佳只能把照片给其中的k个人。佳佳按照与他们的关系好坏的程度给每个人赋予了一个初始权值W[i]。然后将初始权值从大到小进行排序,每人就有了一个序号D[i](取值同样是1--n)。按照这个序号对10取模的值将这些人分为10类。也就是说定义每个人的类别序号C[i]的值为(D[i]-1) mod 10 +1,显然类别序号的取值为1--10。第i类的人将会额外得到E[i]的权值。你需要做的就是求出加上额外权值以后,最终的权值最大的k个人,并输出他们的编号。在排序中,如果两人的W[i]相同,编号小的优先。


输入输出格式

输入格式:

第一行输入用空格隔开的两个整数,分别是n和k。

第二行给出了10个正整数,分别是E[1]到E[10]。

第三行给出了n个正整数,第i个数表示编号为i的人的权值W[i]。

输出格式:

只需输出一行用空格隔开的k个整数,分别表示最终的W[i]从高到低的人的编号。


输入输出样例

输入样例#1: 复制
10 10
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20


输出样例#1: 复制
10 9 8 7 6 5 4 3 2 1


AC:

#include<bits/stdc++.h>
using namespace std;
struct member
{
int w;//初始权值
int index;//初始编号
};
member a[20001];
int b[12];
bool complare1(member x,member y)
{
if(x.w==y.w)return x.index<y.index; //重点
else return x.w>y.w;
}

int main()
{

int n,k;
cin>>n>>k;
for(int i=1;i<=10;++i)cin>>b[i];
for(int i=1;i<=n;++i)cin>>a[i].w,a[i].index=i;

sort(a+1,a+1+n,complare1);
for(int i=1;i<=n;++i)a[i].w+=b[(i-1)%10+1];
sort(a+1,a+1+n,complare1);
for(int i=1;i<=k;++i)cout<<a[i].index<<" ";
return 0;
}
4、对于其他的选排,冒泡,桶排,插排等目前感觉较简单,也可能是做的题少,还没真正用上这些知识。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: