2018-08-17 组合数学

  • A  --  错排

  • Description

HDU 2006'10 ACM contest的颁奖晚会隆重开始了!
为了活跃气氛,组织者举行了一个别开生面、奖品丰厚的抽奖活动,这个活动的具体要求是这样的:
首先,所有参加晚会的人员都将一张写有自己名字的字条放入抽奖箱中;
然后,待所有字条加入完毕,每人从箱中取一个字条;
最后,如果取得的字条上写的就是自己的名字,那么“恭喜你,中奖了!”
大家可以想象一下当时的气氛之热烈,毕竟中奖者的奖品是大家梦寐以求的Twins签名照呀!不过,正如所有试图设计的喜剧往往以悲剧结尾,这次抽奖活动最后竟然没有一个人中奖!
我的神、上帝以及老天爷呀,怎么会这样呢?
不过,先不要激动,现在问题来了,你能计算一下发生这种情况的概率吗?
不会算?难道你也想以悲剧结尾?!

  • Input

输入数据的第一行是一个整数C,表示测试实例的个数,然后是C 行数据,每行包含一个整数n(1<n<=20),表示参加抽奖的人数

  • Output

对于每个测试实例,请输出发生这种情况的百分比,每个实例的输出占一行,结果保留两位小数(四舍五入),具体格式请参照sample output

  • Sample Input

1

2

  • Sample Output

50.00%

  • 题目理解

错误率=错误可能数/总可能数                                                                                                                                                             其中总可能数为n!                                                                                                                                                                               而错误可能数的计算 ,错排思想:

当n个编号元素放在n个编号位置,元素编号与位置编号各不对应的方法数用f(n)表示,那么f(n-1)就表示n-1个编号元素放在n-1个编号位置,各不对应的方法数,其它类推.

  第一步,把第n个元素放在一个位置,比如位置k,一共有n-1种方法;

  第二步,放编号为k的元素,这时有两种情况.

  1. 把它放到位置n,那么,对于剩下的n-2个元素,就有f(n-2)种方法;
  2. 不把它放到位置n,这时,对于这n-1个元素,有f(n-1)种方法;
#include <stdio.h>
const int maxn=35;
int main(){
	int n, m;
       double a1[maxn], a2[maxn];
	scanf("%d", &n);
	while (n--) {
		scanf("%d", &m);
		a1[1] = 0; a1[2] = 1;
		a2[1] = 1; a2[2] = 2;
		for (int i = 3; i <= m; i++) {
			a1[i] = (i - 1) * (a1[i - 1] + a1[i - 2]);
			a2[i] = a2[i - 1] * i;
		}
		printf("%.2lf%%\n", (a1[m] / a2[m]) * 100);

	}
	return 0;
}

 

  • B  -- Halloween treats

  • Description

Every year there is the same problem at Halloween: Each neighbour is only willing to give a certain total number of sweets on that day, no matter how many children call on him, so it may happen that a child will get nothing if it is too late. To avoid conflicts, the children have decided they will put all sweets together and then divide them evenly among themselves. From last year's experience of Halloween they know how many sweets they get from each neighbour. Since they care more about justice than about the number of sweets they get, they want to select a subset of the neighbours to visit, so that in sharing every child receives the same number of sweets. They will not be satisfied if they have any sweets left which cannot be divided.
Your job is to help the children and present a solution.

  • Input

The input contains several test cases.
The first line of each test case contains two integers c and n (1 ≤ c ≤ n ≤ 100000), the number of children and the number of neighbours, respectively. The next line contains n space separated integers a1 , ... , an (1 ≤ ai ≤ 100000 ), where ai represents the number of sweets the children get if they visit neighbour i.
The last test case is followed by two zeros.

  • Output

For each test case output one line with the indices of the neighbours the children should select (here, index i corresponds to neighbour i who gives a total number of ai sweets). If there is no solution where each child gets at least one sweet, print "no sweets" instead. Note that if there are several solutions where each child gets at least one sweet, you may print any of them.

  • Sample Input

4 5

1 2 3 7 5

3 6

7 11 2 5 13 17

0 0

  • Sample Output

3 5

2 3 4

  • 题目理解

这道题主要是题目给出的数据\small n\geq c所以对\small n个前缀和(最多只能有\small c个)进行取模的话要么出现0,可以直接输出这段所有元素;要么出现\small Sum_i%\small C==\small Sum_j%\small C,那么从\small (i+1)-j的元素和就正好等于\small c的倍数

#include<cstdio>
#include<cstring>
const int maxn=100010;
int sum[maxn],a[maxn],pre[maxn];
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)&&(n+m))
    {
        memset(sum,0,sizeof(sum));
        memset(pre,-1,sizeof(pre));
        pre[0]=0;
        int l,r;
        for(int i =1;i<=m;++i)
        {
            scanf("%d",&a[i]);
            sum[i]=(sum[i-1]+a[i])%n;
            if(pre[sum[i]]==-1)
                pre[sum[i]]=i;
            else
            {
                l=pre[sum[i]];
                r=i;
            }
        }
        for(int i=l+1;i<=r;++i)
            if(i!=r)
                printf("%d ",i);
            else
                printf("%d\n",i);
    }
    return 0;
}

 

  • C  -- Dictionary 

  • Description

The isolated people of MacGuffin Island have a unique culture, and one of the most interesting things about them is their language. Their alphabet consists of the first 9 letters of the Roman alphabet (a, b, c, d, e, f, g, h, i). All of their words are exactly 9 letters long and use each of these 9 letters exactly once. They have a word for every possible permutation of these letters. In the library of their most sacred temple is a dictionary, and each word in their language has its own page. By coincidence they order their words exactly as they would be in ordered in English, so the word ‘abcdefghi’ is on the first page, and the word ‘ihgfedcba’ is on the last. The question is, given a list of random words from the MacGuffin language, can you say on which page of the MacGuffin dictionary each appears?

  • Input

The first line of the input file is a positive integer. This integer tells you how many words will follow. The upper limit for this number is 6000. Every subsequent line contains a single word from the MacGuffin language, so if the first number is 1000 there will be 1000 lines after it, each containing a single word.

  • Output

Each line of output will contain an integer. This integer should be the page number for the corresponding word.

  • Sample Input

4

abcdefgih

abcdefghi

abcdefgih

ihgfedcba

  • Sample Output

2

1

2

362880

  • 题目理解

康托展开直接计算,就是对于后面的每一位查找多少小于当前位的值然后将他们放在当前位置得到所有的组合数,对每一位这样操作,将所有组合数相加,得到的就是小于当前排列的个数,加上1以后就是当前排列的序号大小

#include<cstdio>
#include<cstring>
const int maxn=100010;
int sum[maxn],a[maxn],pre[maxn];
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)&&(n+m))
    {
        memset(sum,0,sizeof(sum));
        memset(pre,-1,sizeof(pre));
        pre[0]=0;
        int l,r;
        for(int i =1;i<=m;++i)
        {
            scanf("%d",&a[i]);
            sum[i]=(sum[i-1]+a[i])%n;
            if(pre[sum[i]]==-1)
                pre[sum[i]]=i;
            else
            {
                l=pre[sum[i]];
                r=i;
            }
        }
        for(int i=l+1;i<=r;++i)
            if(i!=r)
                printf("%d ",i);
            else
                printf("%d\n",i);
    }
    return 0;
}

 

  • D  -- How many integers can you find

  • Description

 Now you get a number N, and a M-integers set, you should find out how many integers which are small than N, that they can divided exactly by any integers in the set. For example, N=12, and M-integer set is {2,3}, so there is another set {2,3,4,6,8,9,10}, all the integers of the set can be divided exactly by 2 or 3. As a result, you just output the number 7.

  • Input

There are a lot of cases. For each case, the first line contains two integers N and M. The follow line contains the M integers, and all of them are different from each other. 0<N<2^31,0<M<=10, and the M integer are non-negative and won’t exceed 20.

  • Output

 For each case, output the number.

  • Sample Input

12 2

2 3

  • Sample Output

7

  • 题目理解

查找一个集合里所有元素的倍数的个数,这里运用集合的容斥原理不难的出\small C_1(1)+...+C_1(n)-C_2(1,2)-...-C_2(n-1,n)+C_3(...)+...+C_n(1,2,..,n)其中\small C_2(1,2)表示1,2两个数的公倍数的个数。不难知道所有\small C_i可以使用二进制枚举全部转态,然后通过\small gcd求出最小公倍数,\small n/lcm就是包含在\small 1-n序列中的个数。注意这里有可能出现元素0那么最小公倍数求出来就是0这样的数直接跳过不然除0程序崩溃

#include<cstdio>
const int maxn=50;
typedef long long ll;
ll a[maxn];
ll gcd(ll a,ll b)
{
	return b?gcd(b,a%b):a;
}
int main()
{
    ll n,m,ans,pre,opr;
    while(~scanf("%lld%lld",&n,&m)){
        n--;//小于n
        ans=0;
        for(int i=0;i<m;++i){
            scanf("%lld",&a[i]);
        }
        for(int k=1;k<(1<<m);++k){
            pre=1,opr=0;
            for(int j=0;j<m;++j){
                if(k&(1<<j)){
                    opr++;
                    pre=a[j]/gcd(a[j],pre)*pre;//求出最小公倍数
                }
            }
            if(!pre) continue;
            if(opr&1) ans+=(n/pre);
            else ans-=(n/pre);
            //printf("!!!%d %lld\n",k,ans);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

  • E  -- Ignatius and the Princess III

  • Description

"Well, it seems the first problem is too easy. I will let you know how foolish you are later." feng5166 says.
"The second problem is, given an positive integer N, we define an equation like this:
  N=a[1]+a[2]+a[3]+...+a[m];
  a[i]>0,1<=m<=N;
My question is how many different equations you can find for a given N.
For example, assume N is 4, we can find:
  4 = 4;
  4 = 3 + 1;
  4 = 2 + 2;
  4 = 2 + 1 + 1;
  4 = 1 + 1 + 1 + 1;
so the result is 5 when N is 4. Note that "4 = 3 + 1" and "4 = 1 + 3" is the same in this problem. Now, you do it!"

  • Input

The input contains several test cases. Each test case contains a positive integer N(1<=N<=120) which is mentioned above. The input is terminated by the end of file.

  • Output

For each test case, you have to output a line contains an integer P which indicate the different equations you have found.

  • Sample Input

4

10

20

  • Sample Output

5

42

627

  • 题目理解

采用递归法

不难理解最后一行,\small (n\geq m)对任意\small f(n,m)来说\small (n\geq m)只存在两种情况

  1. 有空盒
  2. 无空盒

又因为盒子无差别,那么对于1来说拿走一个盒子不影响最后的组合数

对于2来说任意盒子拿走一个一个数也不会影响最后的组合数

所以两部分相加就是\small f(n,m)的组合数

#include<cstdio>
#include<cstring>
const int maxn=121;
int dp[maxn][maxn];
int main()
{
    int n;
    memset(dp,0,sizeof(dp));
    for(int i=0;i<maxn;++i)
        dp[0][i]=1;
    for(int i=1;i<maxn;++i)
        dp[i][0]=0;
    for(int i=1;i<maxn;++i){
        for(int j=1;j<maxn;++j){
            if(j>i) dp[i][j]=dp[i][i];
            else dp[i][j]=dp[i][j-1]+dp[i-j][j];
        }
    }
    while(~scanf("%d",&n)){
        printf("%d\n",dp[n][n]);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值