Programming Challenges 习题8.6.3

PC/UVa:110803/10128

Queue

英文题目不好理解,去网上查了一下,意思就是说N个身高不一样的人排成一列,从左边看可以看到P个人,从右边看可以看到R个人,求总的排列数目。

最开始想的是用组合公式,N个人中最高的那个人将队列分成了两部分,左边i个人,从左看可以看见P - 1个人(右边看见多少都行),右边N - 1 - i个人,从右看可以看见R - 1个人(左边看见多少都行),根据乘法原理,两部分相乘,再乘上C(N - 1, i)(最开始想的时候忘记了乘组合数)得到总排列数目,所以问题就变成了给定i个人,从左可以看见j个人的排列方式有多少种。

这个排列数目应该是能递推的。当i - 1个人排好之后,如果第i个人最矮,那么放在最左边,j增加1,其余情况j不变,也就是说公式为g[i][j] = g[i - 1][j - 1] + (i - 1) * g[i - 1][j]。如果第i个人不是最矮,那么可以通过更改前i个人的选择顺序,使得第i个人最矮。

这一点特别重要。如果第i个人的身高可以是任意高度,那么就不能保证在前边的每一种排列方法基础上排第i个人时,所有情况都是等效的,也就是不满足无后效性。

注意这种方法的边界条件g[0][0] = 0

然后还有第二种方法。那就是直接用三维数组表示解,f[n][p][r] = f[n - 1][p - 1][r] + f[n - 1][p][r - 1] + (n - 2) * f[n - 1][p][r]。当第i个人最矮时,TA在最左边则p增加1,在最右边则r增加1,其余位置不变。

注意这种方法的边界条件f[1][1][1] = 1

#include <iostream>

#define MAX_N 13 + 1

using namespace std;

void count1(int T)
{
	/*
	g[i][j] = g[i - 1][j - 1] + (i - 1) * g[i - 1][j]
	*/
	unsigned long long C[MAX_N][MAX_N] = { 0 }, g[MAX_N][MAX_N] = { 0 };
	g[0][0] = 1;
	for (int i = 1; i < MAX_N; i++)
	{
		for (int j = 1; j <= i; j++)
		{
			g[i][j] = g[i - 1][j - 1] + (i - 1) * g[i - 1][j];
		}
	}
	/*
	C[n][k] = C[n - 1][k] + C[n - 1][k - 1]
	*/
	C[0][0] = 1;
	for (int n = 1; n < MAX_N; n++)
	{
		C[n][0] = 1;
		for (int k = 1; k <= n; k++)
		{
			C[n][k] = C[n - 1][k] + C[n - 1][k - 1];
		}
	}
	//1 2 3 4 5 6 7 8 9 10
	int N = 0, P = 0, R = 0;
	for (int t = 0; t < T; t++)
	{
		cin >> N >> P >> R;
		unsigned long long ull = 0;
		for (int m = P; m <= N + 1 - R; m++)
		{
			ull += g[m - 1][P - 1] * g[N - m][R - 1] * C[N - 1][m - 1];
		}
		cout << ull << endl;
	}
}

void count2(int T)
{
	/*
	f[n][p][r] =
	f[n - 1][p - 1][r] +
	f[n - 1][p][r - 1] +
	(n - 2) * f[n - 1][p][r]
	*/
	unsigned long long f[MAX_N][MAX_N][MAX_N] = { 0 };
	f[1][1][1] = 1;
	for (int n = 2; n < MAX_N; n++)
	{
		for (int p = 1; p < MAX_N; p++)
		{
			for (int r = 1; r < MAX_N; r++)
			{
				f[n][p][r] =
					f[n - 1][p - 1][r] +
					f[n - 1][p][r - 1] +
					(n - 2) * f[n - 1][p][r];
			}
		}
	}
	int N = 0, P = 0, R = 0;
	for (int t = 0; t < T; t++)
	{
		cin >> N >> P >> R;
		cout << f[N][P][R] << endl;
	}
}

int main()
{
	int T = 0;
	cin >> T;
	count2(T);
	return 0;
}
/*
3
10 4 4
11 3 1
3 1 2
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值