算法:数字拆分(重复、无重复)



如果单纯是要求拆分数,可用生成函数解决,或者用
动规dp[n][m]=dp[n-m][m-1] + dp[n][m-1](其中n是要划分的数,m是当前最大数)这是不重复的情况,重复的情况,以及特殊数组,最大限制的情况下,只需做少部门改动即可。


无重复:形如

6=1+5=1+2+3=2+4,共3种无重复拆分方式

重复: 形如                6=1+5=1+1+4=1+1+1+3=1+1+1+1+2=1+1+1+1+1=1+1+2+2=1+2+3=2+4=2+2+2=3+3, 共10种重复拆分方式
 
#include<cstdio>
using namespace std;

void dis_number(int num,int min,int a[],int level,int &count)//无重复拆分
{
	for(int i=min;num-i>i;i++){
			count++;
			a[level]=i;

			a[level+1]=num-i;
			for(int j=0;j<=level+1;j++)
				printf("%d ",a[j]);
			printf("\n");

			dis_number(num-i,i+1,a,level+1,count);
	}
}


int dis_number_count(int num)  //动规算拆分次数
{
	int dp[100][100];
	
	for(int i=0; i<=num; i++)
		dp[0][i] = 1;
	for(int i=1; i<=num; i++)
		dp[i][0] = 0;
		
	for(int i=1; i<=num; i++)
		for(int j=1; j<=num; j++)
		{
			if(i<j)
				dp[i][j] = dp[i][i];
			else				
				dp[i][j] = dp[i-j][j-1] + dp[i][j-1];					
		}
	
	return dp[num][num];			
}


void dis_number_same(int num,int min,int a[],int level,int &count)//重复拆分
{
	for(int i=min;num-i>=i;i++){
			count++;

			a[level]=i;
			a[level+1]=num-i;
			for(int j=0;j<=level+1;j++)
				printf("%d ",a[j]);
			printf("\n");

			dis_number_same(num-i,i,a,level+1,count);//重复		
	}

}





int main()
{
loop:

	int num;//输入数
	scanf("%d",&num);
	int count=0;  //拆分计数
	int a[100]; //存储数的拆分
	dis_number(num,1,a,0,count);  //从1开始无重复拆分num
	printf("%d\n",count);
	printf("-----------------\n");

	count=0;
	dis_number_same(num,1,a,0,count); //从1开始重复拆分num
	printf("%d\n",count);
	printf("-----------------\n");

goto loop;

}


--------------------------------------(利用 生成函数 求组合数)

#include<cstdio>
#include<algorithm>
#include<ctime>
using namespace std;

void dis_number(int num,int min,int a[],int level,int &count)//无重复  指数时间
{
	for(int i=min;num-i>i;i++){
			count++;

			//a[level]=i;
			//a[level+1]=num-i;
			//for(int j=0;j<=level+1;j++)
			//	printf("%d ",a[j]);
			//printf("\n");

			dis_number(num-i,i+1,a,level+1,count);
	}
}

void dis_number_same(int num,int min,int a[],int level,int &count)//重复 指数时间
{
	for(int i=min;num-i>=i;i++){
			count++;

			//a[level]=i;
			//a[level+1]=num-i;
			//for(int j=0;j<=level+1;j++)
			//	printf("%d ",a[j]);
			//printf("\n");

			dis_number_same(num-i,i,a,level+1,count);//重复		
	}

}
//如果只是要得到数的拆分数的话,利用生成函数(1+x+x^2+……)(1+x^2+x^4+……)……(1+x^n)中x^n的系数



#define MAXNUM 10000
long long dis_num_same(long long num) //重复 多项式时间
{
	static long long a[MAXNUM+1],b[MAXNUM+1];
	long long *mult=a, *multmp=b;

	for(long long i=0;i<=num;i++)
		mult[i]=1;

	for(long long i=2;i<=num;i++){
		for(long long m=0;m<=num;m++)
			multmp[m]=mult[m];	

		for(long long j=i;j<=num;j+=i)
			for(long long k=0;j+k<=num;k++)
				multmp[j+k]+=mult[k];

		swap(mult,multmp); //交换数组
	}


	return mult[num];
				
}

#define MAXNUM 10000
long long dis_num(long long num)    // 无重复 多项式时间
{
	static long long a[MAXNUM+1],b[MAXNUM+1];
	long long *mult=a, *multmp=b;

	for(long long i=0;i<=1;i++)
		mult[i]=1;
	for(long long i=2;i<=num;i++)
		mult[i]=0;

	int maxvalue=1;

	for(int i=2;i<=num;i++){

		for(int m=0;m<=maxvalue&&m<=num;m++)
			multmp[m]=mult[m];

		for(int m=maxvalue+1;m<=maxvalue+i&&m<=num;m++)
			multmp[m]=0;

		for(int k=0;k<=maxvalue&&i+k<=num;k++)  
			multmp[i+k]+=mult[k];   //maxvalue的使用 使得此处的multmp[i+k]总是预先赋值的了

		maxvalue+=i;	

		swap(mult,multmp);  //数组交换,免去一次赋值

	}

	return mult[num];
				
}

void  dis_num_all(long long num)    // 无重复小于等于num的所有数的无重复拆分方式  多项式时间
{
	static long long a[MAXNUM+1],b[MAXNUM+1];
	long long *mult=a, *multmp=b;

	for(long long i=0;i<=1;i++)
		mult[i]=1;
	for(long long i=2;i<=num;i++)
		mult[i]=0;

	int maxvalue=1;

	for(int i=2;i<=num;i++){

		for(int m=0;m<=maxvalue&&m<=num;m++)
			multmp[m]=mult[m];

		for(int m=maxvalue+1;m<=maxvalue+i&&m<=num;m++)
			multmp[m]=0;

		for(int k=0;k<=maxvalue&&i+k<=num;k++)
			multmp[i+k]+=mult[k];

		maxvalue+=i;	

		swap(mult,multmp);  //数组交换,免去一次赋值

	}

	for(int i=1;i<=num;i++)
		printf("%d: %d\n",i,mult[i]);
				
}




int main()
{
	clock_t ntimestart;
	clock_t ntimeend;
loop:
	int num;//输入数

	scanf("%d",&num);
	//int count=0;  //拆分计数
	//int a[100]; //存储数的拆分
	//ntimestart = clock();
	//dis_number(num,1,a,0,count);  //从1开始无重复拆分num
	//printf("%d\n",count+1);
	//ntimeend = clock();
	//printf("%f\n",(double)(ntimeend - ntimestart)/CLOCKS_PER_SEC);
	//printf("-----------------\n");

	count=0;

	//ntimestart = clock();
	//dis_number_same(num,1,a,0,count); //从1开始重复拆分num
	//printf("%d\n",count+1);
	//ntimeend = clock();
	//printf("%f\n",(double)(ntimeend - ntimestart)/CLOCKS_PER_SEC);

	//ntimestart = clock();
	//printf("%lld\n",dis_num_same(num));
	//ntimeend = clock();
	//printf("%f\n",(double)(ntimeend - ntimestart)/CLOCKS_PER_SEC);

	dis_num_all(num);

	ntimestart = clock();
	printf("%lld\n",dis_num(num));
	ntimeend = clock();
	printf("%f\n",(double)(ntimeend - ntimestart)/CLOCKS_PER_SEC);

goto loop;

}

----------------------------------(引申自微软14,根据特殊数组拆分)
#include<iostream>
#include<algorithm>
using namespace std;

void dis_number_array(int sum,int minpos,int a[], int print_arr[],int level,int &count)//ÎÞÖظ´²ð·Ö
{
	for(int i=minpos;sum-a[i]>i;i++){
			count++;
			print_arr[level]=a[i];

			print_arr[level+1]=sum-a[i];
			for(int j=0;j<=level+1;j++)
				printf("%d ",print_arr[j]);
			printf("\n");

			dis_number_array(sum-a[i],i+1,a,print_arr,level+1,count);
	}
}


int dis_number_array_count(int num, int a[], int k)  //动规算拆分次数
{
	int dp[100][100];
	
	for(int i=0; i<=num; i++)
		dp[0][i] = 1;
	for(int i=1; i<=num; i++)
		dp[i][0] = 0;
		
	for(int i=1; i<=num; i++)
		for(int j=1; j<=num; j++)
		{			
			if(i<j)
				dp[i][j] = dp[i][i];
			else				
				dp[i][j] = (find(a,a+k,j)!=a+k ? dp[i-j][j-1] : 0) + dp[i][j-1];					
		}
	
	return dp[num][num];			
}



void dis_number_array_(int sum,int minpos,int a[], int k,int print_arr[],int level,int &count)//ÎÞÖظ´²ð·Ö
{
	
	if(sum-a[minpos]==0){ //只有到最后才知道可以输出,所以输出程序要放在循环外面 
		count++;
		for(int j=0;j<level;j++)
			cout<<print_arr[j]<<" ";			
		cout<<a[minpos]<<endl;
		return;
	}	
	
	
	for(int i=minpos;i<k&&sum-a[i]>a[i];i++){
			print_arr[level]=a[i];			
			dis_number_array_(sum-a[i],i+1,a,k,print_arr,level+1,count);
	}
	
}



int main()
{
loop:
	int k,sum,count=0,a[100],print_arr[100];
	cin>>sum>>k;
	for(int i=0;i<k;i++)
		cin>>a[i];
	
		
	dis_number_array_(sum,0,a,k,print_arr,0,count);

	cout<<endl<<count<<endl;	
	
goto loop;	
	
	
	return 0;	
}


----------------------------------(微软21,有最大值限制的拆分)
//数列1,2,3..........n, 
//数m
//求m的所有n以内的拆分,这只是数的拆分的特殊情况 
 
#include<iostream>
using namespace std;

void dis_number_nlimit(int sum, int min, int n, int a[],int level,int &count)//无重复拆分
{
	for(int i=min; i<=n && (sum-i>i);i++){
		a[level]=i;
		
		if( sum-i<=n ){
			count++;
			a[level+1]=sum-i;
			
			for(int j=0;j<=level+1;j++)
				printf("%d ",a[j]);
			printf("\n");
		}

		dis_number_nlimit(sum-i,i+1,n,a,level+1,count);
	}
}



int main()
{
loop:

	int m,n;//输入数
	cin>>m>>n;
	
	int count=0;  //拆分计数
	int a[100]; //存储数的拆分
	
	dis_number_nlimit(m,1,n,a,0,count);  //从1开始无重复拆分num
	
	cout<<count<<endl;


goto loop;

}

推荐阅读:

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值