集合的划分(重复状态的消除)

问题 : 集合划分问题

题目描述

S是一个具有n个元素的集合,S={a1a2……an},现将S划分成k个满足下列条件的子集合S1S2……Sk ,且满足:

 

则称 S1 S2 …… Sk 是集合 S 的一个划分。它相当于把 S 集合中的 n 个元素 a1  a2 …… an  放入 k 个( 0 k≤n 30 )无标号的盒子中,使得没有一个盒子为空。请你确定 n 个元素 a1  a2  …… an  放入 k 个无标号盒子中去的划分数 S(n k)

 

输入

输入集合的元素个数n和划分的个数k

输出

输出划分数

样例输入

23 7

样例输出

4382641999117305
思路:之前讲了可以用状态叠加的方法直接叠加每种独立的状态,形成独立的不重复的解,还有一种方法也可以求出此题,先算出所有的方案,再把不可能的方案剪掉,剩下可能但是重复的方案,再除以重复的次数,便可以得出唯一的解,接下来说明怎么求出这个解。由于比较难讲,我先贴代码。

#include <iostream>
#include<algorithm>
#include<cmath>
using namespace std;
long double jc(int k)   //阶乘
{
    int t;
    long double a=1;
    for(t=1;t<=k;t++)
        a*=t;
    return a;
}
long double C(int n,int m)//组合数
{
    long double a=1;
    int t;
    for(t=1;t<=n;t++)
        a=a*(m+1-t)/t;
    return a;
}
long double mpow(int k,int n)//次方
{
    long double a=1;
    int t;
    for(int t=1;t<=n;t++)
        a*=k;
    return a;
}
long double count(int n,int k)
{
    int t;
    long double a;
    a=mpow(k,n);                    //所有情况都先算出来,有重复的
    for(t=1;t<k;t++)                //减去不可能的情况
        a-=count(n,t)*C(t,k);
    return a;
}
int main()
{
    int n,k;
    cin >> n >> k;
    long double a;
    a=count(n,k);
    a=a/jc(k);              //除以重复的情况
    printf("%lldd\n",(long long)a);
    return 0;
}
我们把元素一个一个拿出来,如果是所有解的话每个元素都有k个空间可以存放,有n个元素,所以所有的情况就是n^k,这是包含了不可能的解和重复的解的。

count(n,k)是k个容器里放了n个元素的所有情况。

接下来我们先把不可能的情况减去,由于之前a=mpow(k,n);我们是有可能把所有元素都放在一个容器里,每个容器都有这种情况,假设有7个盘子23个元素,我们把23个元素都放在第一个容器里,把23个元素都放在第二个容器里,都放在第三个容器。。。都放在第七个容器里,由于题目要求每个容器里都至少要有一个元素,所以这些情况都不可能,所以要减去count(n,1)*C(7,1);C(m,n)是组合数,代表了从m个容器里选n个有几种不同方法,同理,也有可能把所有元素都放在两个容器里,那又要减去count(n,2)*C(7,2),因为count(n,2),是两个容器里所有的元素存放的情况了,只是选的两个容器不同而已,这样一直减到count(n,6)*C(7,6);把所有的不可能情况才能减光;

得出通项公式:

Ct(n,k)=k^n-count(n,1)*C(k,1)-count(n,2)*C(k,2)-count(n,3)*C(k,3)-......-count(n,6)*C(k,k-1);这是包含了重复的情况的。

接下来就该考虑怎么消除重复的情况了。

接下来先看几个例子,比如三个元素放在两个容器里。

假设我们先把元素分成两部分,分成1和2,3;

按照之前的算法是有两种情况的。


12,3
第一个容器第二个容器
2,31
第一个容器第二个容器
但其实这是同一种情况。

所以要除以二,实质上看其实元素的排列方式是没变的,只是下面的容器序号变了一下,那么k个容器序号有几种不同的变法呢?显然是k!种所以要除以k!这样剩下的就是唯一的解了。可能说的还不够详细,日后再补充。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值