编程:假设有n个人进行排名,允许并列排名,名次并列人的不同顺序算一种,总共有多少种排名?

本文探讨了一道经典的排名问题,使用动态规划方法求解在允许名次并列的情况下所有可能的排名组合数量。介绍了递归与动态规划的区别,并通过Java代码详细展示了动态规划的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

疯狂游戏笔试一道有趣的编程题:排名问题

题目描述

假设有n个人进行排名,允许并列排名,名次并列人的不同顺序算一种,总共有多少种排名?
例:n=2,输出3,有:a>b,a<b,a=b。

解题思路

这里用到的是动态规划的方法,当然也可以用递归,但是递归计算复杂度高,所以还是用动态规划吧。不了解递归和动态规划区别的自行百度。

首先假设有j个人,名次的个数为i(1 <= i <= j),当i = 1时,j个人排名一样,当i = j时,j个人没有重复的名次,定义f(i,j)为j个人i个名次的排名个数。我们从简单开始想,首先1个人的时候,只有1种排名方式,即f(1,1) = 1; 两个人的时候相当于在1个人的基础上又加了一个人,而新加入的这个人的名次可能和第一个人的名次相同,也可能不同,即f(1,2) = 1,f(2,2) = 2,因为不同的时候有两种情况,同样3个人的时候是在两个人的基础上新加入一个人,以此类推。。。
总结来说,第一种情况,1 < i < j。j个人有i个名次可以看作是j-1个人新加入1个人,j-1个人的名次有两种情况:(1)i个排名,则新加入的人只能是i个名次中的一个,有i种可能;(2)i-1个名次,则新加入的人只能是新的名次,其可以插入在i-1个名次的任意位置,有i种可能;所以可以得到f(i,j) = i×f(i-1,j-1) + i×f(i,j-1)。
第二种情况,i = 1,所有人名次一样,f(1, j) = 1。
第三种情况,i = j,此时进行全排列即可,f(i, i) = i !。

代码

需要注意的是,程序里的下标是从0开始,所以dp[i][j]实际上是j+1个人有i+1个名次的排名个数。

// 以下只为函数体部分,语言为Java
public int numSort(int n) {
	if(n <= 0)
		return 0;
	if(n == 1)
		return 1;
	int result = 0;
	int[][] dp = new int[n][n];   // dp[i][j]表示j+1个人有i+1种排名的个数
	// 行大于列的地方不考虑
	// 初始化第一行,第二种情况
	for(int i = 0; i < n; i++) {
		dp[0][i] = 1;
	}
	// 初始化对角线,全排列,第三种情况
	for(int i = 1; i < n; i++) {
		dp[i][i] = nFac(i+1);
	}
	// 第一种情况
	for(int i = 1; i < n; i++) {
		for(int j = i+1; j < n; j++) {
			dp[i][j] = (i+1)*dp[i][j-1] + (i+1)*dp[i-1][j-1];
		}
	}
	for(int i = 0; i < n; i ++) {
		result += dp[i][n-1];
	}
	return result;
}
// 求n的阶乘
public int nFac(int n) {
	if(n == 0)
		return 1;
	int result = 1;
	while(n > 0) {
		result *= n;
		n -- ;
	}
	return result;
}

就到这里啦。
2019年7月20日,本人的第一篇博客。

1.功能需求 某班有最多不超过30(具体数由键盘输)参加期末考试,最多不超过6门(具体门数由键盘输入)。请使用结构体数组、排序查找法以及模块化程序设计方法编程实现如下菜单驱动的学生成绩管理系统: (1)输入每个学生的学号、姓名和若干门课程考试成绩; (2)计每门课程的总分和平均分; (3)计每个学生的总分和平均分; (4)按每个学生的总分由高到低排出名次表; (5)按每个学生的总分由低到高排出名次表; (6)按学号由小到大排出成绩表; (7)按姓名的字典顺序排出成绩表; (8)按学号查询学生排名及其考试成绩; (9)按姓名查询学生排名及其考试成绩; (10)按优秀(90~100)、良好(80~89)、中等(70~79)、及格(60~69)、不及格(0~59)5个类别,对每门课程分别统计每个类别的数以及所占的百分比; (11)输出每个学生的学号、姓名、各科考试成绩,以及总分和平均分; (12)将每个学生的记录信息写入文件; (13)从文件中读出每个学生的记录信息并显示; 以下为选做功能: (14)向文件中追加1条记录; (15)删除文件中指定学号的记录; (16)修改文件中的某个学生的某门功课的成绩。 2.数据需求 每条记录包含学号(长整型)、姓名(字符串)、多门课程的成绩(浮点型)、总成绩(浮点型)、平均成绩(浮点型)。 数据存储于文件StudentRecord.txt中,格式为每行一条记录;文件的第一行数据为学生数和课程门数。 3.性能需求 系统响应快速,支持至少100条记录的高效处理。 界面友好,操作直观,提供菜单驱动交互。 三、系统设计 1.总体设计 模块划分: 系统采用模块化设计:每个功能模块封装为独立函数;全局数据通过结构体数组统一管理。 主菜单模块:提供功能入口,循环调用各子模块。 具体菜单要求如下: 1.Input record 2.Calculate total and average score of every course 3.Calculate total and average scored of every student 4.Sort in descending order by total score of every student 5.Sort in ascending order by total score of every student 6.Sort in ascending order by number 7.Sort in dictionary order by name 8.Search by number 9.Search by name 10.Statistic analysis for every course 11.List record 12.Write to a file 13.Read from a file 0.Exit Please enter your choice: 数据输入模块:实现学生信息由键盘录入与验证。 数据处理模块:对结构体数组stu进行按条件查询、排序。 文件操作模块:实现对文件中的数据进行读写、增删改等操作。2.数据结构设计 定义结构体Student: typedef struct Student { long num; // 学号 char name[MAX_LEN]; // 姓名 float score[COURSE_NUM]; // 多门课程的成绩 float sum; //每个学生的总分 float aver; // 平均成绩 }STU; 3.法设计 排序法:选择排序实现按平均分排序。 查询法:线性查找实现按学号/姓名查询。 四、系统实现 1.主函数流程 int main(void) { char ch; int n = 0,m = 0; STU stu[STU_NUM]; printf("Input student number(n<%d):",STU_NUM); scanf("%d",&n); printf("Input course number(m<=%d):",COURSE_NUM); scanf("%d",&m); while(1) { ch = Menu(); //显示菜单,并读取用户输入 switch(ch) { case 1 : ReadScore(stu,n,m); break; case 2: AverSumofEveryCourse(stu,n,m); break; case 3: AverSumofEveryStudent(stu,n,m); break; case 4: SortbyScore(stu,n,m,Descending); printf("\nSort in descending order by score:\n"); PrintScore(stu,n,m); break; case 5: SortbyScore(stu,n,m, Ascending); printf("\nSort in ascending order by score:\n"); PrintScore(stu,n,m); break; case 6: AsSortbyNum(stu,n,m); printf("\nsort in ascending order by number:\n"); PrintScore(stu,n,m); break; case 7: SortbyName(stu,n,m); printf("\nSort in dictionary order by name:\n"); PrintScore(stu,n,m); break; case 8: SearchbyNum(stu,n,m); break; case 9: SearchbyName(stu,n,m); break; case 10:StatisticAnalysis(stu,n,m); break; case 11:PrintScore(stu,n,m); break; case 12:WritetoFile(stu,n,m); break; case 13:ReadfromFile(stu,&n,&m); break; case 0:printf("End of program!"); exit(0); default:printf("Input error!"); } } return 0; } 2.各功能函数实现 (1)输入每个学生的学号、姓名和若干门课程考试成绩; (2)计每门课程的总分和平均分; (3)计每个学生的总分和平均分; (4)按每个学生的总分由高到低排出名次表; //按选择法将结构体数组stu的元素按总分升(降)序排序 void SortbyScore(STU stu[],int n,int m,int (*compare)(float a,float b)) { int i,j,k,t; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) { if((*compare)(stu[j].sum,stu[k].sum)) k=j; } if(k!=i) { //说明:可整体交换两个结构体数组元素,也可以数组元素各成员逐一交换 } } } //使数据按升序排序 int Ascending(float a,float b) { return a<b; //这样比较决定了按升序排序,如果a<b, 则交换 } //使数据按降序排序 int Descending(float a,float b) { return a>b; //这样比较决定了按降序排序,如果a>b、则交换 } (5)按每个学生的总分由低到高排出名次表; (6)按学号由小到大排出成绩表; (7)按姓名的字典顺序排出成绩表; (8)按学号查询学生排名及其考试成绩; (9)按姓名查询学生排名及其考试成绩; (10)按优秀(90~100)、良好(80~89)、中等(70~79)、及格(60~69)、不及格(0~59)5个类别,对每门课程分别统计每个类别的数以及所占的百分比; (11)输出每个学生的学号、姓名、各科考试成绩,以及总分和平均分; (12)将每个学生的记录信息写入文件; (13)从文件中读出每个学生的记录信息并显示; 五、测试与调试 1.测试用例 正常输入:输入有效学号、姓名及成绩,验证排序与查询功能。 异常输入:输入非数字成绩,验证错误提示机制。 说明: 1)可以使用多张测试用例表,对不同的功能模块进行测试。在测试用例表中给出正常与异常的测试数据进行功能测试。 2)也可以给出正常或异常测试的运行结果截图 2.调试问题 文件读写错误:文件路径错误→统一使用相对路径并检查权限。
06-19
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值