C语言:考试成绩排名

题目描述:
学校里有N个同学,老师要按照同学们的考试成绩给大家进行排名,一共有多少种不同的排名可能(成绩相同的同学不计名字次序,算一种排名)。
输入描述:
每组数据第一行一个整数N,代表有N同学(1 <= N < 19)。
输出描述:
对于每组数据输出一个整数占一行,代表N个同学之间的排名有多少种可能。
例子:
输入
2
3
输出
3
13
举例:
当N为2时,一共3种排名;当N为3时,一共13种排名。

递归法

假设共有n名同学排出m个名次,则共有f(n,m)个排名方式。

  1. 若m=1,即n名同学共有1个名次,根据题意,此时共有1个排名方式,f(n,m)=1。
  2. 若m <1或m>n,不符合实际情况,此时无法计算排名方式的个数,f(n,m)=0。
  3. 若m>=1且m<=n,求n名同学排出m个名次时排名方式的个数。要令n名同学排出m个名次,那么n-1名同学就应该排出m-1个名次或m个名次。可利用n-1名同学的排名方式来计算总的排名个数(缩小一个问题规模),此时的排名方式分为两种情况:

n-1名同学共排出m-1个名次

这种情况下,要令n名同学排出m个名次,第n名同学的排名应与前m-1个名次均不同,但这个名次的位置共有m种取法。

比如:n-1名同学排出3个名次,分别是1、2、3。第n名同学的名次可选为_1_2_3_中横线的部分,即在4个位置当中选择一个,共4种选择方式。

因此,这种情况下共有f(n-1,m-1)*m种排名方式。

n-1名同学共排出m个名次

这种情况下,要令n名同学排出m个名次,第n名同学的排名应属于前m个名次中的一个。这个名次共有m种取法。

因此,这种情况下共有f(n-1,m)*m种排名方式。

#include<stdio.h>

/**
  * 计算f(n,m)
  * n表示学生人数,m表示名次个数
  */
int compute(int n, int m)
{
	if (m == 1)
		return 1;
	if ((m < 1) || (m > n))
		return 0;

	return compute(n - 1, m - 1) * m + compute(n - 1, m) * m;
}

int main()
{
	int n;
	int sum = 0;

	scanf("%d", &n);

	if ((n < 1) || (n > 19))
		return 0;

	for (int i = 1; i <= n; i++) {
		sum = sum + compute(n, i);
	}
	printf("%d", sum);
	return 0;
}

运行结果:
在这里插入图片描述
在这里插入图片描述

动态规划

递归法解决问题需要消耗大量的栈空间,同时会重复计算很多次。考虑使用动态规划的方法解决此问题。

用动态规划的方法计算学生排名方式的个数f(n,m),可以求出m、n与f(n,m)之间的递推公式。

已知:

当m=n时,f(n,m)=n!		无学生成绩并列
当m=1时,f(n,m)=1		所有学生均并列第一
当1<m<n时,f(n,m) = f(n-1,m-1)*m +f(n-1,m)*m

提前构造f(n,m)函数,利用二维数组计算出需要用到的所有值。

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

#define N 19


int main()
{
	int n;
	int sum = 0;
	int f[N][N];
	memset(f, 0, sizeof(f));

	scanf("%d", &n);

	if ((n < 1) || (n > N))
		return 0;
		
	//初始化f数组
	for (int i = 1; i <= n; i++) {
		f[i][1] = 1;
	}

	for (int i = 2; i <= n; i++) {
		for (int j = 2; j <= i; j++) {
			f[i][j] = f[i - 1][j - 1] * j + f[i - 1][j] * j;
		}
	}

	for (int j = 1; j <= n; j++) {
		sum = sum + f[n][j];
	}

	printf("%d", sum);

	return 0;
}

参考:
https://blog.csdn.net/qq_27952053/article/details/96633796

发布了58 篇原创文章 · 获赞 3 · 访问量 2156

猜你喜欢

转载自blog.csdn.net/weixin_43936250/article/details/104504557