UVA 10844 - Bloques (第二类斯特灵数)

UVA 10844 - Bloques

题目链接

题意:给定n个数字,问这n个数字能分成子集分成有几种分法

思路:一开始先想了个状态,dp[i][j]表示放i个数字,分成j个集合的方案,那么转移为,从dp[i - 1][j - 1]在多一个集合,和从dp[i - 1][j]有j个位置放,那么转移方程为dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j] * j;按理说这个状态转移是没问题的,但是由于这题答案是高精度,n为900时答案高达1700多位,加上高精度运算结果就超时了,后面知道这种是bell数,是可以通过杨辉三角推出来的,具体就是一个数字等于杨辉三角的左边和左上边相加,这样状态转移变成纯高精度加法,然后把高精度加法运算进行压缩位的高精度运算,勉强通过了

代码:

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

using namespace std;
const int MAXN = 1800;

struct bign {
    int len, num[MAXN];

    bign () {
    len = 0;
    memset(num, 0, sizeof(num));
    }
    bign (int number) {*this = number;}
    bign (const char* number) {*this = number;}

    void DelZero ();
    void Put ();

    void operator = (int number);
    void operator = (char* number);

    bool operator <  (const bign& b) const;
    bool operator >  (const bign& b) const { return b < *this; }
    bool operator <= (const bign& b) const { return !(b < *this); }
    bool operator >= (const bign& b) const { return !(*this < b); }
    bool operator != (const bign& b) const { return b < *this || *this < b;}
    bool operator == (const bign& b) const { return !(b != *this); }

    void operator ++ ();
    void operator -- ();
    bign operator + (const int& b);
    bign operator + (const bign& b);
    bign operator - (const int& b);
    bign operator - (const bign& b);
    bign operator * (const int& b);
    bign operator * (const bign& b);
    bign operator / (const int& b);
    //bign operator / (const bign& b);
    int operator % (const int& b);
};

/*Code*/

const int N = 905;
int n;
bign dp[2][N], ans[N];

void init() {
    int pre = 1, now = 0;
    dp[now][1] = 1; ans[1] = 1;
    for (int i = 2; i <= 900; i++) {
    swap(now, pre);
    dp[now][1] = dp[pre][i - 1];
    for (int j = 2; j <= i; j++)
        dp[now][j] = dp[now][j - 1] + dp[pre][j - 1];
    ans[i] = dp[now][i];
    }
}

int main() {
    init();
    while (~scanf("%d", &n) && n) {
    printf("%d, ", n);
    ans[n].Put(); printf("\n");
    }
    return 0;
}

void bign::DelZero () {
    while (len && num[len-1] == 0)
    len--;

    if (len == 0) {
    num[len++] = 0;
    }
}

void bign::Put () {
    printf("%d", num[len - 1]);
    for (int i = len-2; i >= 0; i--)
    printf("%08d", num[i]);
}

void bign::operator = (char* number) {
    len = strlen (number);
    for (int i = 0; i < len; i++)
    num[i] = number[len-i-1] - ‘0‘;

    DelZero ();
}

void bign::operator = (int number) {

    len = 0;
    while (number) {
    num[len++] = number%10;
    number /= 10;
    }

    DelZero ();
}

bool bign::operator < (const bign& b) const {
    if (len != b.len)
    return len < b.len;
    for (int i = len-1; i >= 0; i--)
    if (num[i] != b.num[i])
        return num[i] < b.num[i];
    return false;
}

void bign::operator ++ () {
    int s = 1;

    for (int i = 0; i < len; i++) {
    s = s + num[i];
    num[i] = s % 10;
    s /= 10;
    if (!s) break;
    }

    while (s) {
    num[len++] = s%10;
    s /= 10;
    }
}

void bign::operator -- () {
    if (num[0] == 0 && len == 1) return;

    int s = -1;
    for (int i = 0; i < len; i++) {
    s = s + num[i];
    num[i] = (s + 10) % 10;
    if (s >= 0) break;
    }
    DelZero ();
}

bign bign::operator + (const int& b) {
    bign a = b;
    return *this + a;
}

bign bign::operator + (const bign& b) {
    int bignSum = 0;
    bign ans;

    for (int i = 0; i < len || i < b.len; i++) {
    if (i < len) bignSum += num[i];
    if (i < b.len) bignSum += b.num[i];

    ans.num[ans.len++] = bignSum % 100000000;
    bignSum /= 100000000;
    }

    while (bignSum) {
    ans.num[ans.len++] = bignSum % 100000000;
    bignSum /= 100000000;
    }

    return ans;
}

bign bign::operator - (const int& b) {
    bign a = b;
    return *this - a;
}

bign bign::operator - (const bign& b) {
    int bignSub = 0;
    bign ans;
    for (int i = 0; i < len || i < b.len; i++) {
    bignSub += num[i];
    bignSub -= b.num[i];
    ans.num[ans.len++] = (bignSub + 10) % 10;
    if (bignSub < 0) bignSub = -1;
    }
    ans.DelZero ();
    return ans;
}

bign bign::operator * (const int& b) {
    int bignSum = 0;
    bign ans;

    ans.len = len;
    for (int i = 0; i < len; i++) {
    bignSum += num[i] * b;
    ans.num[i] = bignSum % 10;
    bignSum /= 10;
    }

    while (bignSum) {
    ans.num[ans.len++] = bignSum % 10;
    bignSum /= 10;
    }

    return ans;
}

bign bign::operator * (const bign& b) {
    bign ans;
    ans.len = 0; 

    for (int i = 0; i < len; i++){
    int bignSum = 0;  

    for (int j = 0; j < b.len; j++){
        bignSum += num[i] * b.num[j] + ans.num[i+j];
        ans.num[i+j] = bignSum % 10;
        bignSum /= 10;
    }
    ans.len = i + b.len;  

    while (bignSum){
        ans.num[ans.len++] = bignSum % 10;
        bignSum /= 10;
    }
    }
    return ans;
}

bign bign::operator / (const int& b) {

    bign ans;

    int s = 0;
    for (int i = len-1; i >= 0; i--) {
    s = s * 10 + num[i];
    ans.num[i] = s/b;
    s %= b;
    }

    ans.len = len;
    ans.DelZero ();
    return ans;
}

int bign::operator % (const int& b) {

    bign ans;

    int s = 0;
    for (int i = len-1; i >= 0; i--) {
    s = s * 10 + num[i];
    ans.num[i] = s/b;
    s %= b;
    }

    return s;
}

UVA 10844 - Bloques (第二类斯特灵数)

时间: 2024-11-05 20:36:54

UVA 10844 - Bloques (第二类斯特灵数)的相关文章

hdu 2521 一卡通大冒险 (斯特灵数,贝尔数)

/* 将N张卡分成若干个集合,集合不为空,有多少种分法. f[n][m]表示n张卡分成m组的种类数,那么f[n][m]=f[n-1][m-1]+f[n-1][m]*m,//第二类斯特灵数 而ans[n]=sum{f[n][l]}(1<=l<=m).//ans为贝尔数,Bell数是将P个元素集合分到非空且不可区分例子的划分个数. 其中:f[n-1][m-1]代表第n个人自成一堆: f[n-1][m]*m代表第n个人不自成一堆. */ # include <stdio.h> # inc

HDU2512 一卡通大冒险【斯特灵数,贝尔数】

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2512 题目大意: 有N张卡,将N张卡分成若干不同的集合,集合不能为空.问:总共有多少种分法. 思路: 参考博文:http://blog.csdn.net/acm_cxlove/article/details/7857671 集合的个数可以为1.2.3.-.N.问题就变为了把N张卡放到i个集合中. 这时候个组合问题,可以用第二类斯特灵数解决. S(P,K) = S(P-1,K-1) + K*S(P-

uva 10844 - Bloques(数论+高精度)

题目链接:uva 10844 - Bloques 题目大意:给出一个n,表示有1~n这n个数,问有多少种划分子集的方法. 解题思路:递推+高精度. 1 1 2 2 3 5 5 7 10 15 15 20 27 37 52 dp[i][j]=dp[i?1][j?1]+dp[i][j?1] dp[i][0]=dp[i?1][i?1] ans[i]=dp[i][i] 需要用到高精度,并且缩进. #include <cstdio> #include <cstring> #include &

挑战程序设计竞赛 划分数,贝尔数,斯特灵数

斯特灵数:把n个数划分为恰好k个非空集合的个数,记为S(n,k).且有:S(n,1)=S(n,n)=1. 有递推关系式: S(n+1,k)=S(n,k?1)+kS(n,k?1) 贝儿数:把n个数划分为非空集合的所有划分数.有: Bn=∑i=0nS(n,i) 贝尔数的递推公式: Bn=∑k=0n(nk)Bk 书上的划分数:书上求的是:把n个相同的数划分为不超过m个集合的方法总数.由于这n个数是相同的,就不能算作∑ki=0S(n,i).书上给了这样一个dp的转移方程(定义dp[i][j]为j个数的i

counting the buildings - 第一类斯特灵数

2017-08-10 21:10:08 writer:pprp //TLE #include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> using namespace std; const int INF = 0x3f3f3f3f; typedef long long

Examining the Rooms - 第一类斯特灵数

---恢复内容开始--- 2017-08-10 20:32:37 writer:pprp 题意如下: Recently in Teddy's hometown there is a competition named "Cow Year Blow Cow".N competitors had took part in this competition.The competition was so intense that the rank was changing and changi

Gym 101147G 第二类斯特林数

大致题意: n个孩子,k场比赛,每个孩子至少参加一场比赛,且每场比赛只能由一个孩子参加.问有多少种分配方式. 分析: k>n,就无法分配了. k<=n.把n分成k堆的方案数乘以n的阶乘.N分成k堆得方案数即第二类斯特林数 http://blog.csdn.net/acdreamers/article/details/8521134 #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll

Light OJ 1236 Race 第二类斯特林数

第二类斯特林数 n 匹马 分成1 2 3... n组 每一组就是相同排名 没有先后 然后组与组之间是有顺序的 在乘以组数的阶乘 #include <cstdio> #include <cstring> using namespace std; int dp[1010][1010]; int a[1010]; int main() { a[0] = 1; dp[0][0] = 1; for(int i = 1; i <= 1000; i++) { dp[i][0] = 0; d

swjtu oj Paint Box 第二类斯特林数

http://swjtuoj.cn/problem/2382/ 题目的难点在于,用k种颜色,去染n个盒子,并且一定要用完这k种颜色,并且相邻的格子不能有相同的颜色, 打了个表发现,这个数是s(n, k) * k! s(n, k)表示求第二类斯特林数. 那么关键是怎么快速求第二类斯特林数. 这里提供一种O(k)的算法. 第二类斯特林数: #include <cstdio> #include <cstdlib> #include <cstring> #include <