您的位置:首页 > 其它

nyoj 整数划分(一)(二)

2016-04-14 12:59 218 查看
先来谈谈写这两道题的感受,整数划分(一)刚开始做这道题,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
[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
);


17.
}


18.
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: