VOL4. 天梯赛笔记(1)

1.出生年:新浪微博中一奇葩贴:“我出生于1988年,直到25岁才遇到4个数字都不相同的年份。”也就是说,直到2013年才达到“4个数字都不相同”的要求。本题请你根据要求,自动填充“我出生于y年,直到x岁才遇到n个数字都不相同的年份”这句话。

输入格式:
输入在一行中给出出生年份y和目标年份中不同数字的个数n,其中y在[1, 3000]之间,n可以是2、或3、或4。注意不足4位的年份要在前面补零,例如公元1年被认为是0001年,有2个不同的数字0和1。
输出格式:
根据输入,输出x和能达到要求的年份。数字间以1个空格分隔,行首尾不得有多余空格。年份要按4位输出。注意:所谓“n个数字都不相同”是指不同的数字正好是n个。如“2013”被视为满足“4位数字都不同”的条件,但不被视为满足2位或3位数字不同的条件。

思路:本题目虽然问的是组成年份的数字中不同值的数量,但用数字进行操作难度较大,所以题目的关键在于将年份转为四个字符并判断字符种类的数量,我采用的方法是假设四个字符各不相同,即不同字符数量为4,将第一个字符与其他三个字符依次作比较,若相同,则初始值减一并结束比较,进行第二个字符与其他两个字符的比较。若相同则提前结束比较可以避免字符一字符二相同、字符一字符三相同情况下仍在二和三进行比较时不同值数量减一的问题,即避免了重复判断。
#include<stdio.h>
#include<stdlib.h>

char* judge(int year, int n){
    char* str_year = (char*)malloc(sizeof(char) * 4);
    str_year[0] = (char)(year / 1000);
    str_year[1] = (char)(year / 100 - (year / 1000) * 10);
    str_year[2] = (char)(year / 10 - (year / 1000) * 100 - (year / 100 - (year / 1000) * 10) * 10);
    str_year[3] = (char)(year % 10);
    int num = 4;
    for(int i = 0; i < 4; i++){
        for(int j = i + 1; j < 4; j ++){
            if(str_year[i] == str_year[j]){
                num--;
                break;
            }
        }
    }
    if(num == n) return str_year;
    else return judge(++year, n);
}

int main(){
    int n, year;
    scanf("%d%d", &year, &n);
    char* str_year;
    str_year = judge(year, n);
    int years = (int)str_year[0] * 1000 + (int)str_year[1] * 100 + (int)str_year[2] * 10 + (int)str_year[3];
    printf("%d ",years - year);
    for(int i = 0; i < 4; i++){
        printf("%d", str_year[i]);
    }
    return 0;
}

2.点赞:微博上有个“点赞”功能,你可以为你喜欢的博文点个赞表示支持。每篇博文都有一些刻画其特性的标签,而你点赞的博文的类型,也间接刻画了你的特性。本题就要求你写个程序,通过统计一个人点赞的纪录,分析这个人的特性。

输入格式:
输入在第一行给出一个正整数N(≤1000),是该用户点赞的博文数量。随后N行,每行给出一篇被其点赞的博文的特性描述,格式为“K F​1⋯F​K ”,其中1≤K≤10,F​i(i=1,⋯,K)是特性标签的编号,我们将所有特性标签从1到1000编号。数字间以空格分隔。
输出格式:
统计所有被点赞的博文中最常出现的那个特性标签,在一行中输出它的编号和出现次数,数字间隔1个空格。如果有并列,则输出编号最大的那个。

思路:题目要求的空间及时间复杂度都比较宽松,因此可以采用比较简单的办法,即创建一个大小为1000的数组,统计每个特性标签出现的次数,最后遍历数组找到最后一个最大值(题目要求编号最大的一个)
#include<stdio.h>

int main(){
    int blog_num, label_num, n;
    int count[1000] = {0};
    scanf("%d", &blog_num);
    for(int i = 0; i < blog_num; i++){
        scanf("%d", &label_num);
        for(int j = 0; j < label_num; j++){
            scanf("%d", &n);
            count[n - 1]++;
        }
    }
    int max_ord = 0;
    for(int i = 1; i < 1000; i++){
        if(count[i] >= count[max_ord]) max_ord = i;
    }
    printf("%d %d", max_ord + 1, count[max_ord]);
    return 0;
}

3.情人节:以上是朋友圈中一奇葩贴:“2月14情人节了,我决定造福大家。第2个赞和第14个赞的,我介绍你俩认识…………咱三吃饭…你俩请…”。现给出此贴下点赞的朋友名单,请你找出那两位要请客的倒霉蛋。

输入格式:
输入按照点赞的先后顺序给出不知道多少个点赞的人名,每个人名占一行,为不超过10个英文字母的非空单词,以回车结束。一个英文句点.标志输入的结束,这个符号不算在点赞名单里。
输出格式:
根据点赞情况在一行中输出结论:若存在第2个人A和第14个人B,则输出“A and B are inviting you to dinner…”;若只有A没有B,则输出“A is the only one for you…”;若连A都没有,则输出“Momo… No one is for you …”。

思路:这个题的关键在于字符串的输入与存储,可以用一个二位字符数组来存储所有人的姓名信息。题目中给出每个人名字最长为10个英文字母,所以每一个一维字符串数组的长度定为11(这里使用的是字符串读取而不是字符读取所以要多留一个字符位置用来保存字符串的末尾标志),输入多少个人名我们是不知道的,但他不重要因为只需要知道第14个人是谁,至于其他的名字,就让他们留在缓冲区就好了。所以建立一个[14][11]的字符数组就好了。注意每输入一个名字,要判断一下是否使用’.'结束了输入。
#include<stdio.h>

int main(){
    char name[14][11];
    int i = 0;
    while(1){
        if(i > 13) break;
        scanf("%s", &name[i]);
        if(*name[i] == '.') break;
        getchar();
        i++;
    }
    if(i < 2) printf("Momo... No one is for you ...");
    else if(i < 14) printf("%s is the only one for you...", name[1]);
    else printf("%s and %s are inviting you to dinner...", name[1], name[13]);
}

4.古风排版中国的古人写文字,是从右向左竖向排版的。本题就请你编写程序,把一段文字按古风排版。

输入格式:
输入在第一行给出一个正整数N(<100),是每一列的字符数。第二行给出一个长度不超过1000的非空字符串,以回车结束。
输出格式:
按古风格式排版给定的字符串,每列N个字符(除了最后一列可能不足N个)。

思路:乍一看这个题目是很麻烦的,因为不仅需要按照列进行依次打印,还需要从右向左打印,但解法肯定是利用字符数组存储该字符串,然后利用下标或其他关系做处理进行打印。例如下面的示例:表示空格,限制每列四个字符

在这里插入图片描述

原文是This is a test case,按照字符串进行读入并存储,为了方便记录,对每个字符进行编号是
字符Thisisatestcase
编号0123456789101112131415161718
0123012301230123012
4444333322221111000
根据这样一张表格,我们可以很清晰的看到每一个字符他应该被打印的位置,理论上我们就可以通过对一维的字符串重新写入一个二维数组然后按照顺序输出就可以,但是那样太浪费时间了,直接对一维数组操作更简便一下。注意二三四行的位置是没有字符出现的,所以需要填补一个空格字符。表格应该是下列这样:
字符Thisisatestcase
编号012345678910111213141516171819
01230123012301230123
44443333222211110000
现在我们已经知道了每个字符应该被打印的位置,接下来只需要通过编号即在数组中的物理位置来控制字符在相应的位置输出,为了更好看出编号与打印位置的规律,现在我将二维的数组拉成一维(可以这样做因为我知道他的每一行最多4个字符,所以可以按照顺序每打印四个字符做一次换行)
字符Thisisatestcase
编号012345678910111213141516171819
打印位置161718191516171889101145670123
01230123012301230123
从打印位置与编号的关系可以看出,编号大的先被打印,因此在循环时需要倒序进行,如果用一个循环变量i来控制某个字符在一行中第几个被打印,可以发现,编号-打印位置即i一定被4整除。那这样问题就被解决了,使用一个循环倒序遍历字符数组,当且仅当字符下标-i可以被4整除时才被打印,并且每打印四个字符便换一次行。这里的四是通过键盘键入的,所以并不是针对当前样例进行的分析。
#include<stdio.h>

int main() {
	char str[1001];
	int n;
	scanf("%d", &n);
	getchar();
	scanf("%[^\n]", &str);
	int lenth = 0;
	while (1) {
		if (str[lenth] == '\0') break;
		lenth++;
	}
	while (lenth % n != 0) {
		str[lenth] = (char)(32);
		lenth++;
	}
	for (int i = 0; i < n; i++) {
		for (int j = lenth - 1; j >= 0; j--) {
			if ((j - i) % n == 0) {
				if (str[i] != (char)(32)) printf("%c", str[j]);
				else printf(" ");
			}
		}
		if(i != n - 1) printf("\n");
	}
	return 0;
}
这里还有一个地方需要注意,由于句子中有空格,而使用字符串输入的方式会被空格截断输入,这里用到的这样的控制符scanf("%[^\n]", &str);[]里面是控制符部分,’^'表示除了其后面的字符(这里是回车\n)其他转义字符均被读取,而空格在字符数组存储的形式是ASCII中的32号字符。

5.人以群分:社交网络中我们给每个人定义了一个“活跃度”,现希望根据这个指标把人群分为两大类,即外向型(outgoing,即活跃度高的)和内向型(introverted,即活跃度低的)。要求两类人群的规模尽可能接近,而他们的总活跃度差距尽可能拉开。

输入格式:
输入第一行给出一个正整数N(2≤N≤10​5)。随后一行给出N个正整数,分别是每个人的活跃度,其间以空格分隔。题目保证这些数字以及它们的和都不会超过2​31​​ 。
输出格式:
按下列格式输出:
Outgoing #: N1
Introverted #: N2
Diff = N3
其中N1是外向型人的个数;N2是内向型人的个数;N3是两群人总活跃度之差的绝对值。

思路:从题目描述上来说并不难,只需要按照活跃度对输入数组进行排序,如果数组长度为偶数,则恰好分为两部分,分别求和做差即可;如果数组长度为奇数,为了使总活跃度差距尽可能的拉开,则划为高活跃度的人数应该比低活跃度的人多一个。最开始,我使用了编程难度最低的插入排序,结果有三个测试样例由于超出运行时间没有通过测试样例,很明显这个题考察的是排序算法,我又选择了快速排序的方法,仍然有一个测试样例超出了运行时间,下面是运用了快速排序算法的代码:
#include<stdio.h>
#include<stdlib.h>

void sort(int* arr, int start, int end){
    if(start == end) return;
    int key = arr[start];
    int i = start + 1, j = end, inter;
    while(i < j){
        if(key > arr[j]){
            inter = arr[i];
            arr[i] = arr[j];
            arr[j] = inter;
            i++;
        }
        else j--;
    }
    if(key > arr[i]){
        arr[start] = arr[i];
        arr[i] = key;
    }
    sort(arr, start, i - 1);
    sort(arr, i, end);
}

int main(){
    int num, outgoing = 0, introverted = 0;
    scanf("%d", &num);
    getchar();
    int* arr = (int*)malloc(sizeof(int) * num);
    for(int i = 0; i < num; i++){
        scanf("%d", &arr[i]);
    }
    sort(arr, 0, num - 1);
    if(num % 2 == 0){
        printf("Outgoing #: %d\n", num / 2);
        printf("Introverted #: %d\n", num / 2);
        for(int i = 0; i < num / 2; i++){
            introverted += arr[i];
            outgoing += arr[i + num / 2];
        }
        printf("Diff = %d", outgoing - introverted);
    }
    else{
        printf("Outgoing #: %d\n", num / 2 + 1);
        printf("Introverted #: %d\n", num / 2);
        for(int i = 0; i < num / 2; i++){
            introverted += arr[i];
            outgoing += arr[i + num / 2];
        }
        outgoing += arr[num - 1];
        printf("Diff = %d", outgoing - introverted);
    }
    return 0;
}
```.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值