ECNU OJ 2947 行数据的排序

有 N 行数据,每行有若干数量不等的整数组成。现在要对这 N 行数据排序。排序原则为:首先比较行中的第一个数的值,将第一个数大的行排在前面;若第一个数相等的话,则按照第二个数的值排序(若某行没有第二个数,则该行排在后面);若第二个数还是相等的话,则比较第三个数,依次类推。

例如:

14 38 11 89

27 34

27 12 34

27

92 2 3 1

17 2

排序的结果为:

92 2 3 1

27 34

27 12 34

27

17 2

14 38 11 89

Input

第 1 行:整数 T (1T10) 为问题数

第 2 行:第一个问题的整数 N (1≤N≤1000)

第 3 ∽ N+2 行:第一个问题的每行的数据 ai 和表示行结束的标志-1, 1≤数据个数≤50。0≤ai≤10^9, 数据之间由一个空格分隔。

后面是第 2 ∽ T 个问题的数据。格式与第一个问题相同。

Output

对于每个问题,输出排序后的结果。

格式为:每行输出一行数据,数据之间有一个空格。

#include <stdio.h>

int line_data[1000][51];

//首先比较行中第一个数的值,将第一个数大的行排在前边
//如果第一个数相等,按照第二个数的值排序
//若某行没有第二个数的话,该行排在后边
//如果第二个数还是相等,则比较第三个数,以此类推

int cmp(cont void *a, const void *b)
{
    int *s1 = (int *a);
    int *s2 = (int *b);
    while (*s1 != -1 && s2 != -1 && *s1 = *s2){ //若相等比较下一个,
                                                //若不等,跳出循环
                                                //若为-1,亦跳出循环
        s1++;
        s2++;
    }
    return *s2 - *s1; //跳出循环后,降序排列

}
int main(void)
{
    int n;
    scanf("%d", &n);
    for ( int k = 0; k < n; k++ ){
        int j = 0;
        while(scanf("%d", &line_data[k][j]) && line_data[k][j] != -1){
            j++;
        }        
    }

    qsort(line_data,n, sizeof(line_data[0]), cmp);
    for (int k =0; k < n; k++){
        for ( int j = 0; line_data[k][j+1] != -1; j++ )
            printf("%d ", line_data[k][j]);
        printf("%d", line_data[k][j]);
    }

}

 

ECNU OJ 2896 随机排序

给定一组以一个空格分隔的只含大小写字母的字符串。与普通字典序不同,按照给定的字母顺序对这组字符串排序。设两个字符串的字母不会完全相同。如:Hat、hat、HAt 等不会同时出现。

例如:字母顺序为 QWERTYUIOPASDFGHJKLZXCVBNM 时,一组字符串 hat cat bat book bookworm Dallas Austin Houston fire firefox fumble 的排序结果为:Austin Dallas fumble fire firefox Houston hat cat book bookworm bat。

Input

每组数据由 2 行组成,第 1 行为字母顺序(26 个大写字母),第 2 行是需要排序的一组字符串(只含大小写字母,长度不大于 20)。

数据不多于 100 组。需要排序的一组字符串中包含的字符串个数至少 1 个,至多 100 个。

Output

对于每一组数据,输出排序后的字符串。字符串之间输出一个空格,最后一个字符串后面没有空格,而是输出一个换行符。

#include <stdio.h>

int p[26];
//按给定的字母循序对字符串排序,如果第一个字符相等,则比较第二个字符

int cmp (const void *a, const void *b)
{
    char *s1, *s2; //s1,s2用来遍历字符串
    char ch1, ch2; //ch1,ch2用来指向对应的大写字母
    s1 = (char *)a; 
    s2 = (char *)b;

//--------------------------------重要---------------------------------
    while (*s1 && *s2){
        ch1 = (*s1) >= 'a' ? *s1 - 'a' + 'A' : *s1; // 转化为大写
        ch2 = (*s2) >= 'a' ? *s2 - 'a' + 'A' : *s2; 

        if (p[ch1-'A'] != p[ch2-'A'])   //字符不相等,按顺序表比较
            return p[ch1-'A'] - p[ch2-'A']; //升序
        else { //字符相等,进入下个字符比较
            s1++; s2++;
        }

        if (*s1 == 0) //第一个字符串结束,而第二个未结束则,第一个字符串在前
            return -1;
        else          //第一个字符串未结束,而第二个结束,第一个字符串在后
            return 1;
//--------------------------------重要---------------------------------
    }
}


int main(void)
{

    char s[27];

    while(~scanf("%s\n",s)){   //读入字母顺序表
        for (int i = 0; i < 26; i++)
            p[s[i]-'A'] = i;
        
        char str[2200];
        char a[100][21];

        gets(str);
        int count = 0;
        int i = 0;
//--------------------------------重要---------------------------------
        while (true){ //根据空格分割出每个字符串,把字符串存储在二维数组
            int j = 0;
            while (str[i]!= ' ' && str[i])
                a[count][j++] = str[i++];
            a[count][j] = '\0';
            count++;  //统计字符串个数
            if (!str[i])
                break;
            else
                i++;
        }
//--------------------------------重要---------------------------------
        qsort(a, count, sizeof(a[0]), cmp);
        // qsort(首地址,排序的字符创个数,每一个数组元素大小,比较函数)
        for (int i = 0; i < count - 1; i++)
            printf("%s ", a[i]);
        printf("%s\n", a[i]);
    }
}

 

 

qsort函数和sort函数

先说明一下qsort和sort,只能对连续内存的数据进行排序,像链表这样的结构是无法排序的。

首先说一下, qsort
qsort(基本快速排序的方法,每次把数组分成两部分和中间的一个划分值,而对于有多个重复值的数组来说,基本快速排序的效率较低,且不稳定)。集成在C语言库函数里面的的qsort函数,使用 三 路划分的方法解决排序这个问题。所谓三路划分,是指把数组划分成小于划分值,等于划分值和大于划分值的三个部分。

具体介绍:-^^

void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )
int compare (const void *elem1, const void *elem2 ) );

qsort(即,quicksort)主要根据你给的比较条件给一个快速排序,主要是通过指针移动实现排序功能。排序之后的结果仍然放在原来数组中。
参数意义如下:
第一个参数 base 是 需要排序的目标数组名(或者也可以理解成开始排序的地址,因为可以写&s[i]这样的表达式)
第二个参数 num 是 参与排序的目标数组元素个数
第三个参数 width 是单个元素的大小(或者目标数组中每一个元素长度),推荐使用sizeof(s[0])这样的表达式
第四个参数 compare 就是让很多人觉得非常困惑的比较函数啦。

我们来简单讨论compare这个比较函数(写成compare是我的个人喜好,你可以随便写成什么,比如 cmp 什么的,在后面我会一直用cmp做解释)。 典型的compare的定义是int compare(const void *a,const void *b);
返回值必须是int,两个参数的类型必须都是const void *,那个a,b是随便写的,个人喜好。假设是对int排序的话,如果是升序,那么就是如果a比b大返回一个正值,小则负值,相等返回0,其他的依次类推,后面有例子来说明对不同的类型如何进行排序。

qsort 的使用方法:

一、对int类型数组排序

int num[100];

int cmp ( const void *a , const void *b )

{

  return *(int *)a - *(int *)b;  //升序排序

//return *(int *)b - *(int *)a; //降序排序

/*可见:参数列表是两个空指针,现在他要去指向你的数组元素。所以转型为你当前的类型,然后取值。
        升序排列时,若第一个参数指针指向的“值”大于第二个参数指针指向的“值”,则返回正;若第一个参数指针指向的“值”等于第二个参数指针指向的“值”,则返回零;若第一个参数指针指向的“值”小于第二个参数指针指向的“值”,则返回负。
        降序排列时,则刚好相反。

*/

}

qsort(s,n,sizeof(s[0]),cmp);
//示例完整函数(已在 VC6.0上运行通过):

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int s[10000],n,i;
int cmp(const void *a,const void *b)
{
return(*(int *)b-*(int *)a);  //实现的是升序排序
}
int main()
{

// 输入想要输入的数的个数
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%d",&s[i]);
qsort(s,n,sizeof(s[0]),cmp);
for(i=0;i<n;i++)
printf("%d ",s[i]);
return(0);
}

再说说 sort (常用于 C++ )

sort 使用时得注明:using namespace std; 或直接打 std::sort() 还得加上 #include <algorithm> 头文件

//例:

#include<iostream>
#include<algorithm>
using namespace std;



int main()
{
       int a[20];
   for(int i=0;i<20;++i)
              cin>>a[i];
     sort(a,a+20);             //范围,很明显这里是a+20 注意,这是必要的,如果是a+19
       for(i=0;i<20;i++)        //最后一个值a[19]就不会参与排序。
              cout<<a[i]<<endl;
       return 0;

}

std::sort是一个改进版的qsort. std::sort函数优于qsort的一些特点:对大数组采取9项取样,更完全的三路划分算法,更细致的对不同数组大小采用不同方法排序。

最后,我们来说说sort、qsort的区别:
sort是qsort的升级版,如果能用sort尽量用sort,使用也比较简单,不像qsort还得自己去写 cmp 函数,只要注明 使用的库函数就可以使用,参数只有两个(如果是普通用法)头指针和尾指针;

默认sort排序后是升序,如果想让他降序排列,可以使用自己编的cmp函数

#include<iostream>
#include<algorithm>
using namespace std;
int cmp(int a,int b)
{
  if(a<b)
  return 1; //升序排列,如果改为 a >b,则为降序,要注意sort()中cmp()的返值只有1和0,不像qsort中存在-1!!!!
  else
  return 0;
}


int main(){
    int i;
 int a[20];
 for(int i=0;i<5;++i)
  cin>>a[i];

sort(a,a+5,cmp);          //范围,很明显这里是a+5 注意,这是必要的,如果是a+4最后一个值a[4]就不会参与排序。
for(i=0;i<5;i++)       

cout<<a[i]<<endl;
    system("pause");
 return 0;
}

 

对二维数组的排序:
#include <iostream>
#include <algorithm>
#include <ctime>
using namespace std;

bool cmp(int *p,int *q)
{
    if(p[0]==q[0])
    {
        if(p[1]==q[1])
        {
            return p[2]<q[2];
        }
        else return p[1]<q[1];
    }
    else return p[0]<q[0];
}
int main()
{
    srand(time(0));
    int i;
    int **a=new int*[1000];
    for(i=0;i<1000;++i)
    {
        a[i]=new int[3];
        a[i][0]=rand()%1000;
        a[i][1]=rand()%1000;
        a[i][2]=rand()%1000;
        //printf("%d\t%d\t%d\n",a[i][0],a[i][1],a[i][2]);
    }
    sort(a,a+1000,cmp);
    /*cout<<"After sort"<<endl;
    for(i=0;i<1000;++i)
    {
        printf("%d\t%d\t%d\n",a[i][0],a[i][1],a[i][2]);
    }*/
    return 0;
}

 

刘汝佳第三章习题

3-1 得分 (UVa 1585)

题目:有一个客观的测试结果,如“OOXXOXXOOO”,一个“O”表示一个问题的正确答案,一个“X”表示一个错误的答案。这个测试的每个问题的分数是由它自己计算的,例如,第10个问题的得分是3,它是由它自己和它的前两个连续的O得到的。
因此,“OOXXOXXOOO”的分数是由“1 + 2 + 0 + 0 + 1 + 0 + 0 + 1 + 2 + 3”计算得到的10。
你要编写一个计算测试结果分数的程序。

提示:用一个flag标记是否是O开头

#include <iostream>
using namespace std;

int main(void)
{
    string str;
    cin >> str;
    int len = str.length();
    bool start = false;
    int count = 0;
    int sum = 0;
    for (int i = 0; i < len; i++){
        if (str[i] == 'O'){
            start = true;
            count++;
        } else if (str[i] == 'X') {
            start = false;
            count = 0;
        }
        if (start) {
            sum += count;
        }
    }
}

3-2分子量 (UVa 1586)

题目大意:给出一种物质的分子式(不带括号),求分子量。本题中分子式只包含4种原子,分别为C、H、O、N,

原子量分别为12.01,1.008,16.00,14.01

提示:用数组保存四种原子的分子量,用flag标记当前位是字母还是数字。如果是数字,超过一位的,把每一位的数值从字符串中提取出来。用数组保存每一位的分子量。

#include <stdio.h>
#include <string.h>
#include <ctype.h>
float ans[100];
float mass[] = {12.01, 1.008, 16.00, 14.01};
char str[10];
int len;
int num(int pos, int len)//pos为数字开始的位置,len为分子式长度
{
    int temp;
    for (int i=pos;i<len;i++){ //获取数字长度,并存储到temp
        if(isdigit(str[i])){
            temp=i;
        } else {
            break;
        }
    }

    int n = 0;
    for (int i=pos; i <=temp;i++){ //根据每一十进制位计算分子量
        n = n * 10 +  (str[i] - '0');
    }
    return n - 1; //注意减一是因为最后计算的时候,字母本身占一倍 
  
}

int main(void)
{
    int flag = 0; 
    scanf("%s", str);
    len = strlen(str);
    for (int i = 0; i < len; i++){
        if (str[i] == 'C'){
            ans[i]=mass[0];
        }
        if (str[i] == 'H'){
            ans[i]=mass[1];
        }
        if (str[i] == 'O'){
            ans[i]=mass[2];
        }
        if (str[i] == 'N'){
            ans[i]=mass[3];
        }
        if(isdigit(str[i])&&flag==0){ //初始状态flag = 0,直接进入if判断 
            ans[i]=ans[i-1] * num (i, len); //若该位为数字,则计算数字乘以前面元素的分子量
            flag = 1; //做标记,表示计算数字,下次循环忽略此if判断
        } 
        if (isalpha(str[i])){ //若该位为字母,则标记flag = 0复位,方便下次循环进入上面if判断
            flag = 0;
        }
    }
    
    float sum = 0;
    for (int i = 0; i<len;i++){
       sum+=ans[i];
    }
    printf("%.3f\n",sum);
    return 0;
}

3-3 数数字(UVa1225)

题意:
把前n(n<=10000)个整数顺次写在一起,如n=15时,123456789101112131415
计算0-9各出现了多少次(输出10个数,分别是数字0-9出现的次数)
#include <iostream>
using namespace std;

int main(void)
{
    int sum[10] = {}; //sum[i]表示数字i的个数
    for (int i = 1; i <= 10000; i++){
        int j = i;
        while (j > 0){
            int k;
            k = j % 10; //获取个位
            sum[k]++;   //个位数字的个数加一
            j /= 10;    //下一位
        }
    }

    for  (int i = 0; i < 10; i++){
        cout << i <<  "  : "  << sum[i] << endl;
    }
}

3-4周期串(UVa455)

题目:求一个串的最小循环节。

解题思路:

对一个字符串求其最小周期长度,那么,最小周期长度必定是字符串长度的约数,即最小周期长度必定能被字符串长度整除

其次,对于最小周期字符串,每位都能对应其后周期字串的每一位,

 

if(a[j]!=a[j%3])说明不对应,不是周期,进行下一位扫描。

#include  <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

using namespace std;

char str[104];

int main(void)
{
    int n;
    while (~scanf("%d",&n))
        while (n--){
            scanf("%s", str);
            int len = strlen(str);
            for(int k, i = 1; i < len; i++){
                if (len % i == 0 ) //若子串长度能够整除母串
                    for (k = i; k < len; k++) //开始匹配
                        if (str[i] != str[k%i]) //如果不能匹配
                            break; //则跳出
                if (len == i) {//若全部匹配 
                    cout << i;
                    break;
                }
            }
            if (n) cout << endl;
    }
}

3-8 循环小数(UVa202)

大致题意
输入整数a(0<=a<=3000)和 b (1<=b<=3000),输出a/b的循环小数表示以及循环字节长度。例如a=5,b=43,小数表示为0.(116279069767441860465),循环字节长度为21

#include<stdio.h>  
#include<cstring>  
#include<algorithm>  
#include <iostream>
#define HardBoy main()  
#define ForMyLove return 0;  
using namespace std;  
const int MYDD = 1103+1e4;  
  
int HardBoy {  
    int quotient[MYDD];  
    int remainder[MYDD];  
    int u[MYDD];  
    int n, m, t;  
      
    while(scanf("%d %d", &n, &m) != EOF) {  
        t = n;/*3000以内*/  
        memset(quotient, 0, sizeof(quotient));  
        memset(u, 0, sizeof(u));  
        int cnt = 0;  
        quotient[cnt++] = n/m;  
        n %= m;
        //n为余数,循环前数组u[]被置0,!u[n]为真,进入循环
        while(!u[n] && n) { //u[n] == 0 

            //debug:cout << "before n= " << n << endl;
            u[n] = cnt;
            //一开始数组u[n] = cnt == 0,!u[n]为真,持续循环
            //之后cnt > 0,每次循环,u[n]被赋正值
            //直到循环至n重复出现,即出现循环节,这时u[n]已经被赋值为正值,!u[n]为假,跳出循环
            //debug:cout << "u[n]= " << cnt <<endl;
            remainder[cnt] = n;  //记录余数
            //debug:cout << "reamainder= " << n << endl;
            quotient[cnt++] = 10*n/m;  //记录商
            //debug:cout << "quotient= " << 10*n/m << endl;
            n = 10*n%m;  
            //debug:cout << "after n= " << n << endl;

            //debug:cout << endl; 
        }  
  
        printf("%d/%d = %d.", t, m, quotient[0]);  
        for (int i = 1 ; i < cnt && i <= 50 ; i++) {  
            if (n && remainder[i] == n) printf("(");  
            /*存在循环节->开始存在余数相同的位置*/  
            printf("%d",quotient[i]);  
        }  
        if (!n) printf("(0");
        //余数为零
        if (cnt > 50) printf("...");  
        printf(")\n");  
        printf("   %d = number of digits in repeating cycle\n\n",!n? 1:cnt-u[n]);  
  
    }  
    ForMyLove  
}

3-9 子序列(UVa 10340)

题意:给两个字符串A 和 B。 如果在B中能找到非连续字串和A匹配输出 YES 不能输出NO。
思路B一个个字母遍历过去每对应上A的一个字母就找A的下一个字母直到结束。。

#include <stdio.h>  
#include <string.h>  
  
char a[100005], b[100005];  
int main() {  
    while (~scanf("%s%s", a, b)) {  
        int star = 0, lenb = strlen(b), lena = strlen(a);  
        for (int i = 0; i < lenb; i ++) {  
            if (a[star] == b[i])  
                star ++;  
            if (star == lena) {  
                printf("Yes\n");  
                break;  
            }  
        }  
        if (star != lena)  
            printf("No\n");  
    }  
    return 0;  
}

 

刘汝佳第二章习题

2-1 水仙花数,输出100~999中所有水仙花数

#include <iostream>
using namespace std;

int main (void)
{
    for (int i= 100; i <= 999; i++)
    {
       int a = i / 100;
       int b = ( i % 100 ) / 10;
       int c = i % 10;
       if ( i == a * a * a + b * b * b + c * c *c ){
           cout << i << ' ';
       }
    }
}

2-3 倒三角形 (提示:把图在纸上画出来,数一数几个星号,几个空,找规律

发现对于n层的倒三角形,第i行,有i-1个空格,有2 * n -2 * i + 1个星号即2(n-i)+1

#include <iostream>
using namespace std;

int main(void)
{
    int n;
    cin >> n;
    for ( int i = 1; i <= n; i++) {
        for ( int o = 1 ; o < i; o++ ) {
            cout << ' ';
        }
        
        for (int x = 1; x <= 2 * ( n -i ) + 1; x++) {
            cout << '*';
        }
        cout << endl;
    }
}

2-4 子序列的和

#include 

int main (void)
{
    int a, b;
    float sum=0;
    while(1){
        printf("Please input two numbers\n");
        scanf("%d %d", &a, &b);
        if (a==0 && b==0){
            return 0;
        }
        if (a >= b || b >= 10e6){
            printf("error\n");
            continue;
        } else {
            for (int i=a; i <= b; i++){
                sum+= (1.0/i) * (1.0/i);      
            }
            printf("%.5f\n", sum);
        }
    }
}

2-5 分数化小数

#include <stdio.h>

int main (void)
{
    while(1){
        int a, b, c;
        scanf("%d %d %d", &a, &b, &c);
        if (a == b == c == 0) break;
        printf("%.*f", c, (float) a/b); 
    }
}

2-6排列 (提示,用枚举法。因为三个数字各位的和为定值,乘积也为定值,从123扫描到987 / 3,发现满足上述条件的数字即可)

#include <iostream>
using namespace std;

int calc (int num, int &result_add, int &result_mul)
{
    int i = num / 100;
    int j = (num % 100) / 10;
    int k = num % 10;

    result_add = result_add + i + j + k;
    result_mul = result_mul * i * j * k;
}


int main (void)
{
    int i, j, k;
    int result_add;
    int result_mul;
    
    int sum = 0, pro = 1;
    for (int a = 1; a <= 9; a++) sum += a, pro *= a;

    for ( i = 123; i <= 987 / 3; i++ ){
        j = i * 2;
        k = i * 3;
        
        result_add = 0;
        result_mul = 1;
        calc(i, result_add, result_mul);
        calc(j, result_add, result_mul);
        calc(k, result_add, result_mul);
        if ( result_add == sum && result_mul == pro ){
            cout << i << ' ' << j << ' ' << k << endl;
        }
    }
}

 

刘汝佳第一章习题

1-1 输入3个整数,输出他们的平均值,保留3位小数

#include <iostream>
#include <stdio.h>
using namespace std;

int main(void)
{
    float a, b,c;
    cin >> a >> b >> c;
    printf("%.3f", (a+b+c)/3);
    return 0;
}

1-2 输入华氏温度,输出对应的摄氏温度,保留3位小数

#include <iostream>
#include <stdio.h>
using namespace std;

int main(void)
{
    float f;
    cin >> f;
    printf("%.3f", 5 * ( f - 32 ) / 9 );
    return 0;
}

1-3 输入正整数,输出1+2+…+n的值

#include <iostream>
using namespace std;

int main(void)
{
    int n;
    cin >> n;
    int sum = 0;
    for (int i = 1; i <= n; i++){
        sum += i;
    }
    cout << sum << endl;
}

1-4 正弦和余弦,输入正整数n(n<360),输出n度的正弦、余弦值

#include <stdio.h>
#include <math.h>
int main( void )
{
     while(1){
         int n;
         printf("Please give an angle\n");
         scanf("%d", &n);
         if ( n < 0 || n >= 360 ) {
             printf("error\n");
             continue;
         } else {
             printf("cos(a)=%f, sin(a)=%f\n", cos(n), sin(n));
         }
     }
}

1-5 打折 一件衣服95元,若消费300元,可打85折。输入购买衣服件数,输出需要支付金额,保留两位小数。

#include <stdio.h>

int main( void ) 
{
    while(1){
        printf("Give a number\n");
        int n;
        scanf("%d", &n);
        if (n <=0) {
            printf("error\n");
        } else {
            if ( n * 95 >= 300) {
                printf("%.2f\n", n * 95 * 0.85);
            } else {
                printf("%d\n", n * 95);
            }
        }

    }

}

1-6三角形 输入三角形三边长度,判断是否能构成直角三角形的3个边长

#include <iostream>
using namespace std;

int main(void)
{
    int a, b, c;
    cin >> a >> b >> c;
    if ( ( a + b <= c) || ( a + c <= b )|| ( b + c <=a ) ){
         cout << "no" << endl;
    } else {
        cout << "yes" << endl;
    } 
}

1-7年份 输入年份,判断是否闰年

#include <iostream>
using namespace std;

int main(void)
{
    int year;
    cin >> year;
    if ((y % 4 == 0 && y % 100 != 0) ||y % 400 ==0)
        cout << "yes" << endl;
    else 
        cout << "no"  << endl;
}