转自:整数划分(一)(二)
先来谈谈写这两道题的感受,整数划分(一)刚开始做这道题,dp和递归都不会写,是用深搜写的,不过用深搜写
整数划分(二)就不行了,铁定超时。
昨晚和今晚终于把这两道题的递归和dp全看懂了(看别人博客-_-|||),在这儿重述一下,别人的博客都写得不是太
明白,看了好几遍看不懂,自己又手推好几遍,终于懂了。
先说整数划分(二)
题目:把一个正整数m分成n个正整数的和,有多少种分法?
1.递归,比如将7划分为3个正整数的和,肯定是1,1,5 1,2,4 ,1,3,3, 2,2,3;
这里分成两部分,一部分是包含1,即1,1,5, 1,2,4 1,3,3里面都有1,因此可以划分成1,5, 2,4 3,3(把1除掉) 另一
部分是不包含1,即2,2,3 ,可以将2,2,3分别减去1,即1,1,2,因此递归式就出来了,
digui(7,3)=digui(6,2)+digui(4,3);
即digui(m,n)=digui(m-1,n-1)+digui(m-n,n);
考虑到若n=1,或者m=n,肯定为1,同时若m<n,肯定不存在,故if(n==1||m==n) return 1; if(m<n) return 0;
因此代码:
01.
#include<stdio.h>
02.
#include<string.h>
03.
int
digui(
int
m,
int
n)
04.
{
05.
if
(n==1||n==m)
06.
return
1;
07.
else
if
(m<n)
08.
return
0;
09.
else
10.
return
(digui(m-1,n-1)+digui(m-n,n));
11.
}
12.
int
main()
13.
{
14.
int
N,n,m;
15.
scanf
(
"%d"
,&N);
16.
while
(N--)
17.
{
18.
scanf
(
"%d%d"
,&m,&n);
19.
printf
(
"%d\n"
,digui(m,n));
20.
}
21.
}
2.dp,考虑到digui(m,n)=digui(m-1,n-1)+digui(m-n,n);
可以开一个二维数组,用来存对应的digui的值,即两层for循环,a[i][j]=a[i-1][j-1]+a[i-j][j];
代码:
01.
#include<stdio.h>
02.
#include<string.h>
03.
int
a[101][101];
04.
int
main()
05.
{
06.
int
i,N,j,n,m;
07.
memset
(a,0,
sizeof
(a));
08.
a[1][1]=1;
09.
for
(i=2;i<=100;i++)
10.
for
(j=1;j<=i;j++)
11.
a[i][j]=a[i-j][j]+a[i-1][j-1];
12.
scanf
(
"%d"
,&N);
13.
while
(N--)
14.
{
15.
scanf
(
"%d%d"
,&n,&m);
16.
printf
(
"%d\n"
,a[n][m]);
17.
}
18.
}
1.递归:
digui函数里设置两个形参,digui(int n,int m)代表整数n划分成最大为m的划分个数。
同样举一个栗子,比如样例6,即6刚开始的划分情况中最大数为6,digui(6,6),
6;
5+1;
4+2,4+1+1;
3+3,3+2+1,3+1+1+1;
2+2+2,2+2+1+1,2+1+1+1+1;
1+1+1+1+1+1。
这里digui(6,6)=digui(6,5)+1;
当n=m时,n的划分情况中,m肯定只有一种情况,所以若n=m return 1+gidui(n,m-1);
当n==1||m==1的时候,结果肯定是1;
当n>m时,digui(n,m)=digui(n-m,m)+digui(n,m-1); //比如(6,3)时,对应的应该是3+3,3+2+1,3+1+1+1;
和2+2+2,2+2+1+1,2+1+1+1+1; 1+1+1+1+1+1。 而digui(6-3,3)对应的刚好是3,2+1,1+1+1,而
digui(6,2)对应的刚好是2+2+2,2+2+1+1,2+1+1+1+1; 1+1+1+1+1+1。因此n>m时digui(n,m)=digui(n
-m,m)+digui(n,m-1);
当n<m时,digui(n,m)=digui(n,n);比如digui(6,4)=digui(2,4)+digui(6,3);这时的digui(2,4)应该对应4+2,4+1+1,
即2,1+1;故为digui(2,2);
因此:
01.
#include<stdio.h>
02.
int
digui(
int
n,
int
m)
03.
{
04.
if
(n==1||m==1)
05.
return
1;
06.
if
(n<m)
07.
return
digui(n,n);
08.
if
(n==m)
09.
return
1+digui(n,m-1);
10.
if
(n>m)
11.
return
digui(n,m-1)+digui(n-m,m);
12.
}
13.
int
main()
14.
{
15.
int
N,n;
16.
scanf
(
"%d"
,&N);
17.
while
(N--)
18.
{
19.
scanf
(
"%d"
,&n);
20.
printf
(
"%d\n"
,digui(n,n));
21.
}
22.
}
2.dp
找关系找了好久,终于搞明白了,dp可以开二维然后类似整数划分(二)将n划分成一份,两份,。。。n份,然后
相加,在这里说明一种更为简单的,只需开一个一维的数组即可,
如:
1
2 1+1
3 2+1 1+1+1
4 3+1 2+1+1 2+2 1+1+1+1
5 4+1 3+1+1 2+2+1 1+1+1+1+1
6 5+1 4+2 4+1+1 3+1+1+1 3+2+1 3+3 2+1+1+1+1 2+2+2 2+2+1+1 1+1+1+1+1+1
可以找到规律,比如6对应的第一层是1+(5对应的第一层),第二层是2+(4对应的前两层),第三层是3+(3对应
的前三层)第四层是4+(2对应的前两层) 第五层是5+(1对应的第一层)第六层6+(0);
核心代码只有3行;
具体代码:
01.
#include<stdio.h>
02.
#include<string.h>
03.
int
dp[11];
04.
int
main()
05.
{
06.
int
i,j,N,n;
07.
memset
(dp,0,
sizeof
(dp));
08.
dp[0]=1;
09.
for
(i=1; i<=10; i++)
10.
for
(j=i; j<=10; j++)
11.
dp[j]+=dp[j-i];
12.
scanf
(
"%d"
,&N);
13.
while
(N--)
14.
{
15.
scanf
(
"%d"
,&n);
16.
printf
(
"%d\n"
,dp[n]);
17.
}
18.
}