数的划分

数的划分

数的划分是一个十分经典的递推题,有多种版本。。。

由于本人是 蒟蒻 ,所以整了好长时间还没整懂...

本来早就写好了,因为比较Lazy,就一直没发...

-------------------废话到此结束------------------------------------

其基本骨架即将一个数字  n  划分成 k 份

(一)每分皆不能为空。

1.   k可以是奇数、偶数。

比较经典的思路是:用   f[i][j]表示将一个数字 i 划分成 j 份时的划分方案总数。

则根据划分策略里是否存在 1 ,可以得到递推方程:

f[i][j]=0;(i<=0  ||  j<=0)//此处因为没有任何一个划分出的子集可以是 0 ,所以f(0,x)=0,又因为不会有数字可以划分成 0 份,所以f(x,0)=0;

f( i , j )=f( i-1 , j-1 )+f( i-j , j ) ;

边界:f( 1 , 1)=0;

代码如下:

 

 1 #include<cstdio>
 2 #include<cstring>
 3 const int MAXN=2e2+1;
 4 int f[MAXN][MAXN];
 5 inline int read()
 6 {
 7     int x=0,f=1;char c=getchar();
 8     while(c<'0'||c>'9'){if(c=='-')c=getchar();}
 9     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
10     return f*x;
11 }
12 int find(int n,int k)
13 {
14     if(n<k||n<=0||k<=0)
15         return 0;
16     if(k==1)
17         return 1;
18     int f1=0,f2=0;
19     f1=(f[n-1][k-1]!=-1?f[n-1][k-1]:(f[n-1][k-1]=find(n-1,k-1)));
20     f2=(f[n-k][k]!=-1?f[n-k][k]:(f[n-k][k]=find(n-k,k)));  
21     return f1+f2;
22 }
23 int main()
24 {
25     int n=read(),k=read();
26     //memset(f,-1,sizeof(f));
27     f[0][0]=1;
28     for(int i=1;i<=n;i++)
29         for(int j=1;j<=k;j++)
30         {
31             if(i<j)
32                 f[i][j]=0;
33             if(i-j<j)
34                 f[i][j]=f[i-1][j-1];
35             if(i>=j)
36                 f[i][j]=f[i-1][j-1]+f[i-j][j];
37         }
38     //printf("%d\n",find(n,k));
39     printf("%d",f[n][k]);
40     return 0;
41 }
数的划分

 

2.k只能是奇数或偶数

此时的状态转移方程并没有太大的改变,对于奇数:

f( i , 1 ) = i & 1;

f( i , j )=f (i - 1,j - 1)+f( i - j , j );

偶数:

f(i , 1)=! ( i & 1 );

f(i , j )=f ( i-2 , j-1 ) + f ( i-2*j , j );

个人觉得记忆化搜索比较顺眼。。。

 

(二)允许有集合为空

1.奇数+偶数

首先,很明显,

g( i , j )=∑ f ( i , k )  | 1<=k<=j;

但是如果直接那么计算,很显然会超时 .并不能满足我们的要求 .

考虑递推方程:

这次不能依据是否有1来划分了,应该以“是否放满”划分 :

g[ i , j ]=g[ i , j - 1 ]+g[ i - j , j ];

//解释: 将数字 i 分成 j 份,"可以留空",那么就可以将划分方案分为两类,(1)划分后的各个数字中有 0 ,即"有留空";(2)划分后的数字中 没有0,即"没有留空".

显然,这两种情况互不交叉,且两种情况已经包括了所有情况,因此 答案即为(1)+(2)


"划分后的数字中 有0",,可以先人为制造一个 0 ,即留出一份为空,而将剩下的划分成 j-1 份,所以是 g [ i , j-1 ].

而 "划分后的各个数字中没有 0 ",这种划分可以表示为 g[ i-j ,j ],即先将 j 份每一份都给一个 "1" ,将剩下的 (i-j) 个数字分成 j 份.

 

int find(int n,int k)
{
    if(n<0||k<=0)return 0;
    if(g[n][k]!=-1)return g[n][k];
    else
        if(k==1||n==0)return g[n][k]=1;
        else
            return g[n][k]=find(n,k-1)+find(n-k,k);
}
记忆化搜索程序

2.只能为奇数(偶数)

思路和上面一样的,注意初始化边界即可.

 

PS : 犹记得那个阳光明媚的周六...班主任告诉我去参加物理奥赛初赛,我一脸懵逼:我就从来没学过物理奥赛!但由于其魅(淫)力(威),还是硬着头皮去了...果然成为了mengbier!

做完寥寥几个会做的题,便无聊地推其起了这个题的方程....嗯,物理奥赛还是有收获的!

posted @ 2017-10-27 00:24  昤昽  阅读(466)  评论(0编辑  收藏  举报