洛谷P1655 小朋友的球
题目描述
@发源于 小朋友最近特别喜欢球。有一天他脑子抽了,从口袋里拿出了N个不同的球,想把它们放到M个相同的盒子里,并且要求每个盒子中至少要有一个球,他好奇有几种放法,于是尝试编程实现,但由于他天天不好好学习,只会上B站看游泳教练,于是他向你求助。
输入输出格式
输入格式:
多组数据,每行两个数N,M。
输出格式:
每组数据一行,表示方案数。
输入输出样例
输入样例#1:
4 2 1 1
输出样例#1:
7 1
说明
【样例解释】
N=4,M=2
1,2 3 4
2,1 3 4
3,1 2 4
4,1 2 3
1 2,3 4
1 3,2 4
1 4,2 3
对于20%的数据,满足1≤N,M≤10;
对于100%的数据,满足1≤N,M≤100,数据组数≤10。
stirling数
斯特林数解决的是这样一个问题:
n个不相同的元素,分割为m个集合,每个集合非空【至少有一个元素】,集合无序,问有多少种分割方法?
其实就是题目所述的分球问题
我们令f(n,m)为答案,根据dp的思想,对于第i个球,要么单独放一个盒子,要么在前i-1个球放完后加入其中一个盒子
那么就有f(n,m)=f(n-1,m-1)+j*f(n-1,m)
边界f(i,i)=1,i>=0
f(i,0)=0,i>=1
那么加上高精度就可以解了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#define LL long long int
using namespace std;
const int maxn=105,INF=2000000000;
int N,M;
class BIG{
public:
int n[maxn],len;
BIG() {memset(n,0,sizeof(n));len=0;}
}dp[maxn][maxn];
istream& operator >>(istream& in,BIG& a){
string s;
in>>s;
a.len=s.length();
for(int i=0;i<a.len;i++) a.n[i]=s[a.len-i-1]-'0';
return in;
}
ostream& operator << (ostream& out,const BIG& a){
if(!a.len) cout<<0;
for(int i=a.len-1;i>=0;i--) out<<a.n[i];
return out;
}
BIG operator + (const BIG& a,const BIG& b){
BIG c;
c.len=max(a.len,b.len);
int carry=0,temp;
for(int i=0;i<c.len;i++){
temp=a.n[i]+b.n[i]+carry;
c.n[i]=temp%10;
carry=temp/10;
}
if(carry) c.n[c.len++]=carry;
return c;
}
BIG operator * (const BIG& a,const int& b){
int carry=0,temp;
BIG c;
c.len=a.len;
for(int i=0;i<a.len;i++){
temp=a.n[i]*b+carry;
c.n[i]=temp%10;
carry=temp/10;
}
while(carry) c.n[c.len++]=carry%10,carry/=10;
return c;
}
int main()
{
for(int i=0;i<=100;i++){
dp[i][i].len=dp[i][i].n[0]=1;
}
for(int i=1;i<=100;i++)
for(int j=1;j<=i;j++){
dp[i][j]=dp[i-1][j]*j+dp[i-1][j-1];
}
while(cin>>N>>M) cout<<dp[N][M]<<endl;
return 0;
}