问题 A: 集合划分问题
题目描述
包含n个元素的集合,可以划分为若干个非空子集。例如,当n=3时,集合{1,2,3} 可以构造如下五个划分:
{1,2,3} {1},{2},{3}
{1,2},{3} {1,3},{2} {1},{2,3}
请设计程序,计算包含n个元素的集合可以构造多少个不同的划分。
输入
一个整数n,(1<=n<=25)
输出
一个整数
样例输入
1
样例输出
1
思想:①贝尔数,集合划分是贝尔数的一个问题。②DP,dp[i][j]代表是将i个数分到j个集合内
dp[i][j]=dp[i-1][j]*j+dp[i-1][j-1] 意思是当前有i-1个数和j个集合,你将第i个数放到j个集合中的一个有个j中选择,或者是当前这个数单独放一个集合。
long long B[30];
long long T[30];
const int N = 26;
void Bell()
{
B[0] = 1;
B[1] = 1;
T[0] = 1;
for(int i=2;i<N;i++)
{
T[i-1] = B[i-1];
for(int j=i-2;j>=0;j--)
T[j] = (T[j]+T[j+1]);
B[i] = T[0];
}
}
int main()
{
Bell();
int temp;
scanf("%d",&temp);
printf("%lld\n",B[temp]);
return 0;
}
问题 B: 楼梯台阶问题
题目描述
有一楼梯共M级,刚开始时你在第一级,若每次只能跨上一级或二级,要走上第M级,共有多少种走法?
输入
第一行一个整数T,表示测试组数。
接下来T行每行一个整数M(1<=M<=40)
输出
输出T行
每行一个整数
样例输入
1
4
样例输出
3
思想:第 i 阶台阶由i-1跨一步或者i-2跨2步过来,所以dp[i]=dp[i-1]+dp[i-2].预处理下dp[1] dp[2] dp[3]即可
对于第一阶台阶是0还是1,假如考虑在1不动算一种情况就是1 否则就就是0
问题 C: 小蜜蜂
题目描述
有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行。请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数。其中,蜂房的结构如下所示。
输入
一个整数T表示测试组数。
接下来T行,每行两个整数a和b(0<a<b<50)
输出
T行
每行一个整数
样例输入
2
1 2
3 6
样例输出
1
3
思想:考虑下对于任何点来说它只能通过相邻的2个过来,所以就可以考虑枚举下每个点的贡献,每个点只能对于+1 +2有贡献.
ll dp[60];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(dp,0,sizeof(dp));
int a,b;
scanf("%d%d",&a,&b);
dp[a+1]=1;
dp[a+2]=2;
for(int i=a+3;i<=b;i++)
{
dp[i]=dp[i-1]+dp[i-2];
}
printf("%d\n",dp[b]);
}
return 0;
}
问题 D: 切面条问题
题目描述
一根高筋拉面,中间切一刀,可以得到2根面条。如果先对折1次,中间切一刀,可以得到3根面条。如果连续对折2次,中间切一刀,可以得到5根面条。那么,连续对折n次,中间切一刀,会得到多少面条呢?
输入
一个整数n(1<=n<=1000000)
输出
一个整数,注意,由于答案非常大,要求你输出答案模除1000000007之后的结果
样例输入
3
样例输出
9
思想:单独考虑的每一个话,稳的T,可以将每个点可以推出来一个很好推的式子(毛线) 对于每个n来说 他就等于2^n+1.
无论是线性跑n还是快速幂都OK
问题 E: 整数划分问题
题目描述
计算将一个给定的正整数划分为一系列正整数的和的方案数,称为整数的划分问题,例如,当给定正整数为6时,可以有如下划分:
6=6;
6=5+1;
6=4+2=4+1+1;
6=3+3=3+2+1=3+1+1+1;
6=2+2+2=2+2+1+1=2+1+1+1+1;
6=1+1+1+1+1+1+1。
如果用f(n)代表正整数n的划分数,则f(6)=11
现在,给你数字n,要求你计算f(n)
输入
一个整数n
输出
输出f(n)。注意,由于答案非常大,你的答案需要输出模除1000000007之后的结果
样例输入
6
样例输出
11
思想:母函数算法的模板题,如果看到此博客可以学一下母函数,会在其中注释代码,仍有不懂可以提问。
const long long mod = 1e9+7;
const int maxn= 1e5+5;
long long c1[maxn];
long long c2[maxn];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<=n;i++)//c1是储存每个的结果 c2是每次计算的结果
{
c1[i]=1; //初始化对于每一项的系数都是1
c2[i]=0;
}
for(int i=2;i<=n;i++)//枚举项数 总共有n个式子
{
for(int j=0;j<=n;j++)//枚举前边的到的所有项数
{
for(int k=0;k+j<=n;k+=i)//对于第i个式子的每个式子的系数 所以是每次加i
c2[k+j]=(c1[j] + c2[k+j])%mod; //k+j是乘完之后的次方
}
for(int k=0;k<=n;k++)//进行替换
{
c1[k]=c2[k];
c2[k]=0;
}
}
printf("%lld\n",c1[n]%mod);
return 0;
}
问题 F: 找单词(母函数)
题目描述
假设有x1个字母A, x2个字母B,..... x26个字母Z,同时假设字母A的价值为1,字母B的价值为2,..... 字母Z的价值为26。那么,对于给定的字母,可以找到多少价值<=50的单词呢?单词的价值就是组成一个单词的所有字母的价值之和,比如,单词ACM的价值是1+3+14=18,单词HDU的价值是8+4+21=33。(组成的单词与排列顺序无关,比如ACM与CMA认为是同一个单词)。
输入
输入首先是一个整数N,代表测试实例的个数。
然后包括N行数据,每行包括26个<=20的整数x1,x2,.....x26.
输出
对于每个测试实例,请输出能找到的总价值<=50的单词数,每个实例的输出占一行。
样例输入
2
1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
9 2 6 2 10 2 2 5 6 1 0 2 7 0 2 2 7 5 10 6 10 2 10 6 1 9
样例输出
7
379297
思想:Hdu上的母函数的入门题。跟上边的那个差不多只是一些东西稍微改了下,思想还是一样的,套上板子改改就好了。
对于母函数不懂的可以看上题题解,看懂在看此题更好。
const int maxn= 60;
long long c1[maxn];
long long c2[maxn];
int num[maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
for(int i=1;i<=26;i++)//输入每种的数量
scanf("%d",&num[i]);
memset(c1,0,sizeof(c1));
memset(c2,0,sizeof(c2));
for(int i=0;i<=num[0];i++)//将第一个的为1 和 当初的那个n一样
c1[i]=1;
for(int i=1;i<=26;i++)//枚举项 就是那个26项
{
for(int j=0;j<=50;j++)//因为要求不超过50 所以最大的权值应该是50
for(int k=0;k<=num[i]*i && k+j<=50;k+=i)//k的值应该小于等于最大的那个 而且 不会超过50
c2[k+j]=(c1[j] + c2[k+j]);
for(int k=0;k<=50;k++)
{
c1[k]=c2[k];
c2[k]=0;
}
}
long long ans=0;
for(int i=1;i<=50;i++)//所有的求一个和
ans+=c1[i];
printf("%lld\n",ans);
}
return 0;
}