动态规划-整数划分

1.基础dp 整数划分

1.1基础题目1:输入一个正数n,输出所有和为n连续正数序列。

例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-5、4-6和7-8。

思路:等差数列的求和,an=a1+k-1 ;(a1+a1+k-1)*k=2*n; 先求出k即找出2*n的因子;

解的集合与 2n的在[2,sqrt(2n)]区间的几个因子相关。每个因子可能对应一个解-----a1;也可能没有解;等差数列的性质 假设 a1最小为1,则(a1+a1+k-1)*k/2=(k+1)*k/2;要满足(k+1)*k不超过2*n;

即解最大为sqrt(2*n)+1;最小为2;k可以理解为等差序列中元素的个数。

伪代码:1) imax=sqrt2*n+1

2) i=2~imax

3) 如果i可以被(2*n)整除,令temp=2*n-i*i+i;之后判断如果2*i也可以被temp整除,(说明可以a1有解,即a1= temp/(2*i) ;

4) start=temp/(2*i);end=start+i-1;

5) 输出 j=start~end;

6) 循环 3-5

C语言实现:

#include<stdio.h>
#include<math.h>
int main()
{
	int n,temp,start,end;
	while(~scanf("%d",&n))
	{
		int imax=(int)sqrt(n*2)+1;
		for(int i=2;i<imax;i++)
		{
			if((n*2)%i==0)
			{
				temp=n*2-i*i+i;
				if(temp%(i*2)==0)
			   { 
				start=temp/(i*2);
				end=start+i-1;
				printf("%d = ",n);//打印输出
				for(int j=start;j<end;j++)
				printf("%d+",j);
				printf("%d\n",end);
			   }
			}
		}
	}
}

1.1基础题目2_跳台阶问题(Fibonacci序列)
题目:一个台阶总共有n级,如果一次可以跳1级,也可以跳2级。求总共有多少总跳法,并分析算法的时间复杂度;

f(1)=1; f(2)=2;f(n)=f(n-1)+f(n-2),(n>2);

//递归代码

int fib(int n)
{
	if(n==1)return 1;
	if(n==2)return 2;
	return fib(n-1)+fib(n-2);
}

1.1正题——整数划分

是指把一个正整数n写成如下形式:

 n=m1+m2+...+mi; (其中mi为正整数,并且1 <= mi <= n),则{m1,m2,...,mi}为n的一个划分。

如果{m1,m2,...,mi}中的最大值不超过m,即max(m1,m2,...,mi)<=m,则称它属于n的一个m划分。这里我们记n的m划分的个数为f(n,m);

例如但n=4时,他有5个划分,{4},{3,1},{2,2},{2,1,1},{1,1,1,1};

注意4=1+3 和 4=3+1被认为是同一个划分。

该问题是求出n的所有划分个数,即f(n, n)。

下面我们考虑求f(n,m)的方法;

 根据n和m的关系,考虑以下几种情况:

1)当 n = 1 时,不论m的值为多少(m > 0 ),只有一种划分即 { 1 };

2)当 m = 1 时,不论n的值为多少,只有一种划分即 n 个 1,{ 1, 1, 1, ..., 1 };

3)当 n = m 时,根据划分中是否包含 n,可以分为两种情况:  

(a). 划分中包含n的情况,只有一个即 { n }

      (b). 划分中不包含n的情况,这时划分中最大的数字也一定比 n 小,即 n 的所有 ( n - 1 ) 划分。

   因此 f(n, n) = 1 + f(n, n-1);

(4) 当 n < m 时,由于划分中不可能出现负数,因此就相当于 f(n, n);

(5) 但 n > m 时,根据划分中是否包含最大值 m,可以分为两种情况:

    (a). 划分中包含 m 的情况,即 { m, { x1, x2, ..., xi } }, 其中 { x1, x2, ..., xi } 的和为 n - m,可能再次出现 m,因此是(n - m)的 m 划分,因此这种划分个数为 f(n-m, m);

(b). 划分中不包含 m 的情况,则划分中所有值都比 m 小,即 n 的 ( m - 1 ) 划分,个数为 f(n, m - 1);

因此 f(n, m) = f(n - m, m) + f(n, m - 1);

问题1:求整数划分的个数

//递归代码

//递归代码
int div(int n,int m)
{
	if(n==1||m==1)
	return 1;
	if(n<m)
	return div(n,n);
	if(n==m)
	return 1+div(n,m-1);
	if(n>m)
	return div(n-m,m)+div(n,m-1);
}
//打表法,记忆搜索
#include<stdio.h>
#include<string.h>

int a[100][100];//记忆化搜索 

int fun(int n,int m)
{
	if(a[n][m]>0)
	{	//	printf("Use the a[%d][%d]\n",n,m);
		return a[n][m];
	}
	if(n==1)
	return a[1][m]=1;
	if(m==1)
	return a[n][1]=1;
	if(n<m)
	return a[n][n]=fun(n,n);
	if(n==m)
	return a[n][m]=fun(n,m-1)+1;
	if(n>m)
	return a[n][m]=fun(n-m,m)+fun(n,m-1);
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		memset(a,-1,sizeof(a));
		int n;
		scanf("%d",&n);
		printf("%d\n",fun(n,n));
	}
	return 0;
}

问题 2:输出整数划分的元素?

#include<stdio.h>
#include<string.h>
int tp;
int div(int n,int m)
{
	if(n==1||m==1){ 
	if(n==1)
	printf("1\n");
	else if(m==1)
	{
		int j=tp-n;
		if(j!=0)
		printf("%d ",j);
		while(n--)
		printf("1 ");
		printf("\n");
	} 
	return 1;
   }
	if(n<m){
		//printf("%d\n",n); 
		return div(n,n);
	}
	if(n==m)
	{
		printf("%d\n",n);
		return 1+div(n,m-1);
	}
	if(n>m){
		printf("%d ",m);
		return div(n-m,m)+div(n,m-1);
		printf("\n");
	}
}
int main()
{
	int n;
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		tp=n;
		printf("%d\n",div(n,n));
	}
}

NYOJ176整数划分(二)

时间限制:1000 ms  |  内存限制:65535 KB

难度:3

描述

把一个正整数m分成n个正整数的和,有多少种分法

例:把5分成3个正正数的和,有两种分法:

1 1 3

1 2 2

输入

第一行是一个整数T表示共有T组测试数据(T<=50)
每组测试数据都是两个正整数m,n,其中(1<=n<=m<=100),分别表示要拆分的正数和拆分的正整数的个数。

输出

输出拆分的方法的数目。

样例输入

2

5 2

5 3

样例输出

2

2

#include<stdio.h>
int dp[101][101]={1};
int main()
{
	for(int i=1;i<101;i++)
		for(int j=1;j<=i;j++)
		dp[i][j]=dp[i-1][j-1]+dp[i-j][j];
	int t,n,m;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		printf("%d\n",dp[n][m]);
	} 
	 return 0;  
}

整数划分终极版

NYOJ571整数划分(三)

时间限制:1000 ms  |  内存限制:65535 KB

难度:5

描述

整数划分是一个经典的问题。请写一个程序,完成以下要求。

输入

每组输入是两个整数n和k。(1 <= n <= 50, 1 <= k <= n)

输出

对于输入的 n,k;
第一行: n划分成若干正整数之和的划分数。
第二行: n划分成k个正整数之和的划分数。
第三行: n划分成最大数不超过k的划分数。
第四行: n划分成若干个 奇正整数之和的划分数。
第五行: n划分成若干不同整数之和的划分数。

第六行: 打印一个空行

样例输入

5 2

样例输出

7

2

3

3

3

提示

样例输出提示:
1.将5划分成若干正整数之和的划分为: 5, 4+1, 3+2, 3+1+1, 2+2+1, 2+1+1+1, 1+1+1+1+1
2.将5划分成2k个个正整数之和的划分为: 3+2, 4+1
3.将5划分成最大数不超过2的划分为: 1+1+1+1+1, 1+1+1+2, 1+2+2
4.将5划分成若干 奇正整数之和的划分为: 5, 1+1+3, 1+1+1+1+1
5.将5划分成若干不同整数之和的划分为: 5, 1+4, 2+3

 

#include<stdio.h>
int d1[101][101];//若干整数之和与最大不超过 k的划分
int d2[101][101];//将 n划分成 k个不同整数之和的划分 
int d4[101][101];//将 n划分成若干奇整数之和的划分 
int d5[101][101];//将 n划分成若干不同整数之和的划分 
void divone(){//将 i划分为最大元素不超过 j的划分个数 
	int i,j;
	d1[0][0]=1;
	for(i=0;i<101;++i)
	for(j=1;j<101;++j)
	{
		if(i<j)d1[i][j]=d1[i][i];
		else d1[i][j]=d1[i-j][j]+d1[i][j-1];
	}
} 
void divtwo(){//将 i划分成 j个不同整数之和的划分 
	int i,j;
	d2[0][0]=1;
	for(i=1;i<=100;++i)
	for(j=1;j<=i;++j)
	d2[i][j]=d2[i-1][j-1]+d2[i-j][j];
}

void divfour(){//将 i划分成若干奇整数之和的划分
	int i,j;
	d4[0][0]=1;
	for(i=0;i<101;++i)
	for(j=1;j<101;++j)
	{
		if(j&1){
			if(j>i)d4[i][j]=d4[i][i];
			else d4[i][j]=d4[i-j][j]+d4[i][j-1];
		}
		else d4[i][j]=d4[i][j-1];
	}
}
void divfive(){//将 i划分成若干不同整数之和的划分
	int i,j;
	d5[0][0]=1;
	for(i=0;i<=100;++i)
		for(j=1;j<=100;++j)
		if(j>i)d5[i][j]=d5[i][i];
		else d5[i][j]=d5[i-j][j-1]+d5[i][j-1];//与divone()不同_d5[i-j][j-1] 
}
int main()
{
	int n,k;
	divone();
	divtwo();
	divfour();
	divfive();
	while(~scanf("%d%d",&n,&k))
	{
		printf("%d\n%d\n%d\n%d\n%d\n",d1[n][n],d2[n][k],d1[n][k],d4[n][n],d5[n][n]);
	}
}

NYOJ746整数划分(四)

时间限制:1000 ms  |  内存限制:65535 KB

难度:3

描述

       暑假来了,hrdv 又要留学校在参加ACM集训了,集训的生活非常Happyps:你懂得),可是他最近遇到了一个难题,让他百思不得其解,他非常郁闷。。亲爱的你能帮帮他吗?

      问题是我们经常见到的整数划分,给出两个整数 n , m ,要求在 n 中加入m - 1 个乘号,将n分成m段,求出这m段的最大乘积

输入

第一行是一个整数T,表示有T组测试数据
接下来T行,每行有两个正整数 n,m ( 1<= n < 10^19, 0 < m <= n的位数);

输出

输出每组测试样例结果为一个整数占一行

样例输入

2

111 2

1111 2

样例输出

11

121

代码如下:

//先用两重循环计算a[i][j],表示ij这段子串的数值,dp[i][j]表示到i的这个前缀子串分为j部分的乘积的最大值,

//则有dp[i][j]=max(dp[i][j],dp[k][j-1]*a[k+1][i]),注意j-2<=k<i; 

#include<stdio.h>
#define LL long long
#include<cstring> 
#include<algorithm>
using namespace std;
char s[25];
LL a[25][25];
LL dp[25][25];

int main()
{
	int t,m,i,j,k;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%s%d",s,&m);
		int len=strlen(s);
		memset(a,0,sizeof(a));
		memset(dp,0,sizeof(dp));
		for(i=0;i<len;i++)
		{
			a[i][i]=s[i]-'0';
			for(j=i+1;j<len;j++)
			{
				a[i][j]=a[i][j-1]*10+(LL)(s[j]-'0');
			}
		}
		for(i=0;i<len;i++)
		{
			dp[i][1]=a[0][i];
			for(j=2;j<=m;j++)
			{
				for(k=j-2;k<i;k++)
				{
					dp[i][j]=max(dp[i][j],dp[k][j-1]*a[k+1][i]);
				}
			}
		}
		printf("%lld\n",dp[len-1][m]);
	}
	return 0;
}






阅读更多
个人分类: ----acm----动态规划
上一篇周赛(三)_dp_01背包_1423: 贪婪戈尔曼
下一篇hdu-2955_Robberies_动态规划-01背包(浮点型,需转换背包容量)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭