信息学奥赛一本通(C++版) 第二部分 基础算法 第二章 数据排序

 信息学奥赛一本通(C++版) 第二部分 基础算法  第二章 数据排序

http://ybt.ssoier.cn:8088/index.php

1311    【例2.5】求逆序对 此题不适合 初学者 做 2018-5-17

//ybt 1310 【例2.2】车厢重组
//洛谷 P1116 车厢重组
//题目若不标难度,看起来很吓人,排序的算法很多,会造成比较次数不同
//归并排序 需要空间,从题意看提供不了
//该题只给了一个交换空间,基本上属冒泡或快排
//极端情况,快排与冒泡算法复杂度相同
//并且交换的元素是相邻的,故只有冒泡系列了,该题,难在思路,写写很快.
//2017-10-28 9:12 AC
#include <stdio.h>
int a[10100];
int main(){
    int n,i,j,t,cnt=0;
    scanf("%d",&n);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++)
            if(a[i]>a[j])t=a[i],a[i]=a[j],a[j]=t,cnt++;
    printf("%d",cnt);
    return 0;
}
//1311 【例2.5】求逆序对
//按冒泡思路编写,猜想,要超时,试试,看看数据水不水
//提交,果然超时,
//翻了之前的记录,发现是洛谷 P1908 逆序对 不过当时是用树状数组
//http://blog.csdn.net/mrcrack/article/details/61625530
//这次准备用归并排序做
//http://blog.csdn.net/yuehailin/article/details/68961304代码写得很对胃
//http://www.cnblogs.com/chengxiao/p/6194356.html用图分析归并排序算法过程,很清晰
//http://blog.csdn.net/acdreamers/article/details/16849761用归并排序求逆序对,关键点讲得好,摘抄如下:
//归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来。
//在合并的过程中(设l<=i<=mid,mid+1<=j<=h),当a[i]<=a[j]时,并不产生逆序数;当a[i]>a[j]时,在
//前半部分中比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并
//排序中的合并过程中计算逆序数.
//样例通过,提交,未通过,
//同样的程序在洛谷里提交,AC,真是太奇葩了,洛谷 P1908 逆序对
//https://www.luogu.org/problemnew/show/P1908
//转念一想,可能是逆序对太多,int溢出,把int 改成long long 修改,提交AC 真不容易啊2017-10-28 22:26
//将int 改成 long long 整整用了17分钟
#include <stdio.h>
int a[100100],b[100100];
long long ans=0;////转念一想,可能是逆序对太多,int溢出,把int 改成long long
void memery_sort(int left,int mid,int right){//自小到大
    int i=left,j=mid+1,n=mid,m=right,k=0;
    while(i<=n&&j<=m)
        if(a[i]>a[j]){
            ans+=n-i+1;
            b[k++]=a[j++];
        }else
            b[k++]=a[i++];
    while(i<=n)b[k++]=a[i++];
    while(j<=m)b[k++]=a[j++];
    for(i=0;i<k;i++)a[left+i]=b[i];//1此处写成 for(i=1;i<k;i++)a[i]=b[i];
}
void merge_sort(int left,int right){
    int mid=(left+right)/2;
    if(left>=right)return ;
    merge_sort(left,mid);
    merge_sort(mid+1,right);
    memery_sort(left,mid,right);
}
int main(){
    int n,i;
    scanf("%d",&n);
    for(i=0;i<n;i++)
        scanf("%d",&a[i]);
    merge_sort(0,n-1);
    printf("%lld",ans);
    return 0;
}

//【例2.5】求逆序对
//采用枚举的方式,猜测能得30分,试试看
//逆序对数量:极限情况 (10^5+0)/2*10^5=5*10^9 cnt采用int要溢出,故cnt采用long long
//提交,测试点1,测试点5-10 运行超时,还算满意2018-3-11 14:45
//高效的逆序对求解方法有两种,归并排序,树状数组,重心先放在归并排序上。
//先举了较为简单的例子1 4 3 2,对归并排序进行了模拟。
//编起代码,模拟还是帮助挺大的
//第一步,编写归并排序代码
//第二步,找逆序对
//样例通过,提交AC 2018-3-11 17:27
#include <stdio.h>
#define LL long long
int a[100100],n,b[100100];
LL cnt=0;
void memory_sort(int left,int mid,int right){//自小到大排序
    int i=left,j=mid+1,n=mid,m=right,k=0;//请注意k=0
    while(i<=n&&j<=m)
        if(a[i]>a[j])b[k++]=a[j++],cnt+=n-i+1;//n-i表示i+1,i+2,...,n-1,n这些数据的个数,n-i+1表示加上i数据
        else b[k++]=a[i++];
    while(i<=n)b[k++]=a[i++];
    while(j<=m)b[k++]=a[j++];
    for(i=0;i<k;i++)a[left+i]=b[i];
}
void merge_sort(int left,int right){
    int mid=(left+right)/2;
    if(left>=right)return;//只剩1个或0个数据,返回
    merge_sort(left,mid);//递归
    merge_sort(mid+1,right);//递归
    memory_sort(left,mid,right);//排序
}
int main(){
    int i,j;
    scanf("%d",&n);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    merge_sort(1,n);
    printf("%lld",cnt);
    return 0;
}


//1176 谁考了第k名
#include <stdio.h>
struct node{
    int id;
    double score;
}stu[110],stu_t;
int main(){
    int n,k,i,j;
    scanf("%d%d",&n,&k);
    for(i=1;i<=n;i++)scanf("%d%lf",&stu[i].id,&stu[i].score);
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++)
            if(stu[i].score<stu[j].score){
                stu_t=stu[i];
                stu[i]=stu[j];
                stu[j]=stu_t;
            }
    printf("%d %g",stu[k].id,stu[k].score);
    return 0;
}

//1177 奇数单增序列
#include <stdio.h>
int a[510];
int main(){
    int n,i,j,b,k=0,t;
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        scanf("%d",&b);
        if(b%2==1)k++,a[k]=b;
    }
    for(i=1;i<=k;i++)
        for(j=i+1;j<=k;j++)
            if(a[i]>a[j])t=a[i],a[i]=a[j],a[j]=t;
    printf("%d",a[1]);
    for(i=2;i<=k;i++)
        printf(",%d",a[i]);
    return 0;
}

//1178 成绩排序
#include <stdio.h>
#include <string.h>
struct node{
    char name[30];
    int score;
}stu[30],stu_t;
int main(){
    int n,i,j;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%s%d",stu[i].name,&stu[i].score);
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++)
            if(stu[i].score<stu[j].score)stu_t=stu[i],stu[i]=stu[j],stu[j]=stu_t;
            else if(stu[i].score==stu[j].score&&strcmp(stu[i].name,stu[j].name)>0)
                stu_t=stu[i],stu[i]=stu[j],stu[j]=stu_t;
    for(i=1;i<=n;i++)
        printf("%s %d\n",stu[i].name,stu[i].score);
    return 0;
}


1179 奖学金

3.洛谷 p1093 奖学金

http://blog.csdn.net/mrcrack/article/details/61625530

NOIP 2007 普及组 复赛 scholar 奖学金

1.采用结构体,该题思路就会比较清晰。

2.因总数不超过300,因条件考虑较多,采用冒泡排序处理,代码处理起来比较简单。

3.按照题目要求进行程序书写,即可,注意单独写一个交换函数,可以减少代码量。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
struct node{
    int i;//序号
    int yu;//语文
    int shu;//数学
    int ying;//英语
    int zong;//总分
}stu[300+10],stu_t;
void swap(int i,int j){
    stu_t=stu[i];
    stu[i]=stu[j];
    stu[j]=stu_t;
}
int main(){
    int n;
    int i,j,yu,shu,ying;
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        scanf("%d%d%d",&yu,&shu,&ying);
        stu[i].i=i;
        stu[i].yu=yu;
        stu[i].shu=shu;
        stu[i].ying=ying;
        stu[i].zong=yu+shu+ying;
    }
    for(i=1;i<=n;i++)//自大到小排序
        for(j=i+1;j<=n;j++)
            if(stu[i].zong<stu[j].zong)
                swap(i,j);
            else if(stu[i].zong==stu[j].zong)
                if(stu[i].yu<stu[j].yu)
                    swap(i,j);
                else if(stu[i].yu==stu[j].yu)
                    if(stu[i].i>stu[j].i)
                        swap(i,j);
    for(i=1;i<=5;i++)
        printf("%d %d\n",stu[i].i,stu[i].zong);
    return 0;
}

1180 分数线划定

3.//洛谷 p1068 分数线划定

http://blog.csdn.net/mrcrack/article/details/61625530

NOIP 2009 普及组 复赛 score 分数线划定

1.向下取整,猜测5.5为5.问题读完,发现理解正确。

2.看了数据范围n=9000,n^2=8.1*10^7,用冒泡排序,超时可能性极大,快排出手。

3.采用结构体记录志愿者信息。

4.用快排编写不舒服,马上转向冒泡排序。

5.提交全WA,才发现测试语句未删除,删除后提交,发现错了测试点2,测试点10.

6.找到问题:

if(p[i].s>=p[q-1].s)//此处写成 if(p[i].s>=p[q].s)错了测试点2,测试点10

printf("%d %d\n",p[q-1].s,count);//此处写成 if(p[i].s>=p[q].s)错了测试点2,测试点10

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
struct node{
    int k;
    int s;
}p[10000],mid,t;

int main(){
    int n,m,q;
    int i,j;
    int count=0;
    scanf("%d%d",&n,&m);
    for(i=0;i<n;i++)
        scanf("%d%d",&p[i].k,&p[i].s);
    for(i=0;i<n;i++)
        for(j=i+1;j<n;j++)
            if(p[i].s<p[j].s){
                t=p[i];
                p[i]=p[j];
                p[j]=t;
            }else if(p[i].s==p[j].s){
                if(p[i].k>p[j].k){
                    t=p[i];
                    p[i]=p[j];
                    p[j]=t;
                }
            }
    q=m*1.5;
    for(i=0;i<n;i++)
        if(p[i].s>=p[q-1].s)//此处写成 if(p[i].s>=p[q].s)错了测试点2,测试点10
            count++;
        else
            break;
    printf("%d %d\n",p[q-1].s,count);//此处写成 if(p[i].s>=p[q].s)错了测试点2,测试点10  
    for(i=0;i<count;i++)
        printf("%d %d\n",p[i].k,p[i].s);
    return 0;

}

//1181 整数奇偶排序
#include <stdio.h>
int a[20],b[20];
int main(){
    int i,j,d,m=0,n=0,t;
    while(scanf("%d",&d)!=EOF)
        if(d%2==1)a[m++]=d;
        else b[n++]=d;
    for(i=0;i<m;i++)
        for(j=i+1;j<m;j++)
            if(a[i]<a[j])t=a[i],a[i]=a[j],a[j]=t;
    for(i=0;i<n;i++)
        for(j=i+1;j<n;j++)
            if(b[i]>b[j])t=b[i],b[i]=b[j],b[j]=t;
    for(i=0;i<m;i++)
        printf("%d ",a[i]);
    for(i=0;i<n;i++)
        printf("%d ",b[i]);
    return 0;
}

//1182 合影效果
#include <stdio.h>
#include <string.h>
double a[50],b[50];
char s[20];
int main(){
    int n,i,j,p=0,q=0;
    double d,t;
    scanf("%d",&n);
    for(i=0;i<n;i++){
        scanf("%s%lf",s,&d);
        if(strcmp(s,"male")==0)a[p++]=d;
        else b[q++]=d;
    }
    for(i=0;i<p;i++)
        for(j=i+1;j<p;j++)
            if(a[i]>a[j])t=a[i],a[i]=a[j],a[j]=t;
    for(i=0;i<q;i++)
        for(j=i+1;j<q;j++)
            if(b[i]<b[j])t=b[i],b[i]=b[j],b[j]=t;
    for(i=0;i<p;i++)printf("%.2lf ",a[i]);
    for(i=0;i<q;i++)printf("%.2lf ",b[i]);
    return 0;
}

//1183 病人排队
//分成老年人,非老年人两组
//该题需要自己标记登记序列
#include <stdio.h>
#include <string.h>
struct node{
    char id[20];
    int age;
    int seq;
}a[110],b[110],t;
int main(){
    int n,i,j,d,p=0,q=0,k=0;
    char s[20];
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        scanf("%s%d",s,&d);
        k++;
        if(d>=60)strcpy(a[p].id,s),a[p].age=d,a[p].seq=k,p++;
        else strcpy(b[q].id,s),b[q].age=d,b[q].seq=k,q++;
    }
    for(i=0;i<p;i++)
        for(j=i+1;j<p;j++)
            if(a[i].age<a[j].age)t=a[i],a[i]=a[j],a[j]=t;
            else if(a[i].age==a[j].age&&a[i].seq>a[j].seq)t=a[i],a[i]=a[j],a[j]=t;
    for(i=0;i<q;i++)
        for(j=i+1;j<q;j++)
            if(b[i].seq>b[j].seq)t=b[i],b[i]=b[j],b[j]=t;
    for(i=0;i<p;i++)
        printf("%s\n",a[i].id);
    for(i=0;i<q;i++)
        printf("%s\n",b[i].id);
    return 0;
}


1184 明明的随机数

//1184 明明的随机数
//之前代码不好理解,决定重编
//样例通过,提交AC 2018-5-5 15:27
#include <stdio.h>
int a[110],b[110];
int main(){
    int n,i,j,t,k;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(i=1;i<=n;i++)//自小到大排序
        for(j=i+1;j<=n;j++)
            if(a[i]>a[j]){
                t=a[i];
                a[i]=a[j];
                a[j]=t;
            }
    b[1]=a[1],k=1;
    for(i=2;i<=n;i++)
        if(a[i]!=a[i-1])//去除重复数据
            b[++k]=a[i];
    printf("%d\n",k);
    for(i=1;i<=k;i++)
        printf("%d ",b[i]);
    return 0;
}


2.洛谷 p1059 明明的随机数

http://blog.csdn.net/mrcrack/article/details/61625530

NOIP 2006 普及组 复赛 random 明明的随机数

1.本题考查排序,因<=100,采用写法比较简单的冒泡排序。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
int main(){
    int n;
    int i,j,t;
    int a[100+10];
    int b[100+10];
    int count;
    scanf("%d",&n);
    for(i=0;i<n;i++)
        scanf("%d",&a[i]);
    for(i=0;i<n;i++)//冒泡排序,自小到大 
        for(j=i+1;j<n;j++)
            if(a[i]>a[j]){
                t=a[i];
                a[i]=a[j];
                a[j]=t;
            }
    count=1;
    b[0]=a[0];
    for(i=1;i<n;i++)
        if(b[count-1]!=a[i])
            b[count++]=a[i];
    printf("%d\n",count);
    printf("%d",b[0]);
    for(i=1;i<count;i++)
        printf(" %d",b[i]);
    printf("\n"); 
    return 0;

}

//1185 单词排序 2017-10-28 13:19
#include <stdio.h>
#include <string.h>
char a[110][60];
int main(){
    int k=0,i,j,p;
    char t[60];
    while(scanf("%s",a[k])!=EOF)k++;
    for(i=0;i<k;i++)
        for(j=i+1;j<k;j++)
            if(strcmp(a[i],a[j])>0)strcpy(t,a[i]),strcpy(a[i],a[j]),strcpy(a[j],t);
            else if(strcmp(a[i],a[j])==0){//单词左移
                k--;
                for(p=i;p<k;p++)
                    strcpy(a[p],a[p+1]);
            }
    for(i=0;i<k;i++)
        printf("%s\n",a[i]);
    return 0;
}


//1186 出现次数超过一半的数
#include <stdio.h>
#include <string.h>
int a[110];
int main(){
    int n,i,b,k,max=-1,tag=0;
    memset(a,0,sizeof(a));
    scanf("%d",&n);
    for(i=0;i<n;i++){
        scanf("%d",&b);
        a[b+50]++;
    }
    for(i=0;i<100;i++)
        if(a[i]>=(n+1)/2)tag=1,printf("%d ",i-50);
    if(tag==0)printf("no");
    return 0;
}

//1187 统计字符数
#include <stdio.h>
#include <string.h>
int a[30];
char s[1100];
int main(){
    int i,len,max=-1,j;
    memset(a,0,sizeof(a));
    scanf("%s",s);
    len=strlen(s);
    for(i=0;i<len;i++)a[s[i]-'a']++;
    for(i=0;i<26;i++)
        if(a[i]>max)j=i,max=a[i];
    printf("%c %d",'a'+j,max);
    return 0;
}
该章节总结:除了 1311 【例2.5】求逆序对 其他问题都可以用冒泡排序解决。

是练冒泡排序的好章节。

2017-10-28 22:26 AC该章节内容

猜你喜欢

转载自blog.csdn.net/mrcrack/article/details/78372905