解题报告 之 SOJ1942 Foto

20 篇文章 2 订阅

解题报告 之 SOJ1942 Foto


Description

The election campaign just started, so PSOS decided to make some propagation. One of the representatives came with a great idea - he proposes to make an photography of their Parliament Club. Unfortunatelly, even after many briefings, the representatives are still not able to agree upon an ordering of people in the photography. Moreover, there are a lot of representatives and it is not possible to have all of them in a single picture. The situation became critical. In the end, the representatives decided they will make one photo of every possible combination of people and their ordering. Different photographs will be used for a large number of billboards PSOS plan to use. To make things more clear, every person gets a Unique Identification Number (UIN). Every picture can be then described as the succession of several UINs x1, x2, ... xk, in whichxi is UIN of the i-th person in the picture x. Now we can sort all the possible photographs (combinations) to a single succession. The ordering of combinations of the same length (photographs with the same number of people in it) is defined as follows: the combination p is greater than the combination q if there exists any i such as that pj = qj for every j < i, and pi > qi. Your goal is to find the right place for a given picture among all possible photographs.

Input Specification

At the first line there is a positive integer N stating the number of assignments to follow. Each assignment consists of exactly two lines. At the first line of each assignment, there are two integers n and k1 <= k <= n <= 12 stating the total number of representatives (n) and the number of them which can fit into a single picture (k). At the second line of the assignment, there is exactly k positive numbers x1, x2, ... xk, each of them 1 <= xi <= n. No number can appear more than once on this line.

Output Specification

For each assignment, output the text "Variace cislo I ma poradove cislo J." (Combination #I is J-th in sequence). Fill the number of assignment instead of I (starting with one), and the number of the given photograph among all possible combinations after ordering, instead of J (also starting with one).

Sample Input

4
1 1
1
5 1
4
3 3
1 2 3
5 3
5 3 1

Output for Sample Input

Variace cislo 1 ma poradove cislo 1.
Variace cislo 2 ma poradove cislo 4.
Variace cislo 3 ma poradove cislo 1.
Variace cislo 4 ma poradove cislo 55.

题目大意:有kase组数据,每组数据给出n,表示一共有几个数(1~n)。再给出k,表示要从中选取几个数字。现在给出你一个数字排列,问你按照字典序从小到大排序,这个排列是第几大的?


分析:感觉就是一道很简单的排列数的题,我们试图找到给出序列之前的序列数,再加一即可。比如5 3 5 3 1这组数据,5个数选3个,问5 3 1是第几大的?首先我们看,要找比531小的数,有两个途径,第一个就是把第一个数换掉,然后后面随便排列,第二个就是保持5不变,后面递推用一个策略继续找。然后答案就是把这两个答案加起来。

第一个途径,对于某一位,我们怎么找到能有几种方案呢?还是用5 3 1举例子,一开始进行到5,5前面都保持不变(其实5没有前面),那么5前面比他大的数有0个,5前面比他小的数有0个,没用的比5小的数有4个,那么先C(4,1);第二步,5后面的任意排列,即从剩下没选的4个数中选两个任意排列,即A(4,2),那么答案的一部分就是C(4,1)*A(4,2)。然后按照这个方法一直往后加到最后一位即可。

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

int greaterb4[15];
int num[15];

long long A( int a, int b )
{
	long long res = 1;
	for(int i = a; i > a-b; i--)
	{
		res *= i;
	}
	return res;
}

int main()
{
	int kase;
	cin >> kase;
	int n, k;
	for(int step = 1; step <= kase; step++)
	{
		memset( greaterb4, 0, sizeof greaterb4 );
		cin >> n >> k;
		for(int i = 1; i <= k; i++)
		{
			cin >> num[i];
			for(int j = 1; j < i; j++)
			{
				if(num[j]>num[i]) greaterb4[i]++; //统计前面有几个更大的数
			}
		}//输入

		long long ans = 0;

		for(int i = 1; i <= k; i++)
		{
			long long tem = num[i]-(i-greaterb4[i]); //C(num,1) 其中num表示比这个数小且前面还没用过的数有多少个
			tem *= A( n - i, k - i );								//剩下的数全排列
			ans += tem;
		}
		printf( "Variace cislo %d ma poradove cislo %lld.\n", step, ans + 1 );
	}
	return 0;
}

题解到此结束。另外既然做到了就再顺便存一个超级叼的排列组合数模板。感觉可以O(n)时间预处理再在O(1)时间求出排列组合数。

首先生成运算基,无论排列组合数都是一样的。即调用fac()函数生产运算基。
然后要排列数和组合数只需要分别对运算基进行不同的运算即可。注意都是double的运算,也注意算最终结果的时候要加上exp。

#include<iostream>
#include<cmath>
#include<cstdio>
using namespace std;

const int MAXN = 1e6;
double f[MAXN];

void fac()
{
	f[0] = 0;
	for(int i = 1; i < MAXN; i++)
	{
		f[i] = f[i - 1] + log( i*1.0 ) ;
	}
}

double getA( int n, int m ) //计算A(n,m) 的值,n为右下角的数,m为右上角的数
{
	return f[n] - f[n - m];
}

double getC( int n, int m )//计算C(n,m) 的值,n为右下角的数,m为右上角的数
{
	return f[n] - f[m] - f[n - m];
}

int main()
{
	fac();
	int n, m;
	while(cin >> n >> m)
	{
		printf( "A(%d,%d) = %lf\n", n, m, exp( getA( n, m ) ) );
		printf( "C(%d,%d) = %lf\n", n, m, exp( getC( n, m ) ) );
	}
	return 0;
}

嗯,存个模板,暂时这个样吧!~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值