1. 程式人生 > >整數劃分問題---動態規劃、遞迴

整數劃分問題---動態規劃、遞迴

第一:

將一個整數 n 劃分為 不超過m 組 的劃分數 如  n=4 m=3 輸出: 4 { 1+1+2=1+3=2+2=4} 思路:使用動態規劃: 定義狀態: dp[i][j] j的i劃分的組數 遞推:dp[i][j]=dp[i][j-i]+dp[i-1][j] ------當m=n時,變成了常見的整數劃分問題
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1000+10;
int dp[maxn][maxn],n,m;
void sovle(){
	dp[0][0]=1;
	for(int i=1;i<=m;i++){
		for(int j=0;j<=n;j++){
			if(j-i>=0){
				dp[i][j]=dp[i][j-i]+dp[i-1][j];
			}
			else dp[i][j]=dp[i-1][j];
		}
	}
	
}
int main()
{
	memset(dp,0,sizeof(dp));
	n=4;m=4;
	sovle(); 
	cout<<dp[m][n]<<endl;
}

第二:

我們用遞迴+記憶化的方法來解決普通整數劃分問題:定義 f(n,m)為將整數n劃分為一系列整數之和,其中加數 最大不超過m。 得到下面的遞推關係式:  當n==1 || m==1 只有一種劃分,即 1 或者 1+1+1......+1 當m>n 顯然,等價於 f(n,n) 當m==n 此時:我考慮加數包含m與否的兩種情況: 1)劃分不包含m,即f(n,m-1)---所有m-1的劃分 2)劃分包含 m,此時只有一種即 m 所以當m==n時,有 f(n,m)=f(n,m-1)+1 當m<n時, 1)包含m時,{m,x1,x2,x3....xi}此時 等價於 f(n-m,m) 2)不包含m時,顯然f(n,m-1)
所以f(n,m)=f(n,m-1)+f(n-m,m) 採用記憶化技術優化複雜度:
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1000+10;
int f[maxn][maxn];
int  getspilit(int n,int m)
{
	if(n==1||m==1)return 1;
	if(m>n){
		if(f[n][m]!=-1)return f[n][m];
		return f[n][m]=getspilit(n,n);
	}
	if(n==m){
		if(f[n][m]!=-1)return f[n][m];
		return f[n][m]=getspilit(n,m-1)+1;
	} 
	return f[n][m]=(getspilit(n-m,m)+getspilit(n,m-1));
}
int main()
{
	int n=4,m=4;
	memset(f,-1,sizeof(f));
	cout<<getspilit(n,m)<<endl;
	return 0;
}

第三:

將整數n劃分為一系列連續的整數之和即: 15=7+8     =4+5+6     =1+2+3+4 這裡我們假設劃分之後最小的整數位x ,那麼 x+(x+1)+(x+2)......+(x+m)假設一共有i個整數,整理得: x*i+i*(i-1)/2=n i=1,2,3.....其中i的限制條件為:s1=i*(i-1)/2<=n,只有當x為整數時才有可能。
#include<iostream>
#include<cstring>
using namespace std;
int getsplit(int n)
{
	int i,s1,s2,x,sum=0;
	for(i=1;(s1=i*(i-1)/2)<=n;i++)
	{
		s2=n-s1;
		x=s2/i;
		if(x==0)break;
		if(s2%i==0)
		{
			cout<<x<<" ";
			for(int j=1;j<i;j++)cout<<x+j<<" ";
			cout<<endl;
			sum++;
		}
	}
	return sum;
}
int main()
{
	int n=15;
	cout<<getsplit(n)-1<<" methods to split the number."<<endl;
}