zoukankan      html  css  js  c++  java
  • 整数划分 以hoj1402为例

    hoj1402整数划分问题
    整数划分是一个经典的问题。希望这道题会对你的组合数学的解题能力有所帮助。

    Input

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

    Output

    对于每组输入,请输出六行。

     

    第一行: 将n划分成若干正整数之和的划分数。

    第二行: 将n划分成k个正整数之和的划分数。

    第三行: 将n划分成最大数不超过k的划分数。

    第四行: 将n划分成若干奇正整数之和的划分数。

    第五行: 将n划分成若干不同整数之和的划分数。

    第六行: 打印一个空行。

     

    看明白第一行的程序后,后面试着写,果断好使,哈哈

    第一行和第三行:

    对于第一行和第三行,根据状态转移方程很容易写出:

    dp[i][j] = dp[i][i]            j>i时

    = dp[i-j][j]+dp[i][j-1]   i>=j时

    i记录的是该要分的数,j表示最大数不超过j的划分数,当j比i还大时当然是只能j分到i而已,

    当i>=j时,注意到5 = 5 = 4+1 = 3+2 = 3+1+1 = 2+2+1 = 2+1+1+1 = 1+1+1+1+1,5可以划分为

    4+1,而4的划分数之前已求出,相当于加上4的划分数,即dp[i][j-1],另外注意到

    5 = 2+2+1 = 2+1+1+1 = 1+1+1+1+1,即还要加上要减的数5减掉2之后,

    dp[5][2] = dp[5-2][2]+dp[5][1] = dp[3][2]+1 = dp[3-2][2]+dp[3][1]+1 = dp[1][1]+1+1 = 3;

    每次算dp时其实都可以先写写dp之间的转移关系,弄清楚后,再写出状态转移方程

     

    第二行:

    将n划分成K个正整数之和的划分数。根据第一行和第三行的程序的启发,可以这样设置dp为三维,

    dp[i][p][j],i表示要划分的数,p为要划分的总个数,j为划分的数中的最大值为不超过j,可以得到

    以下状态转移方程:

    dp[i][p][j] = dp[i][p][i]            j>i时

    = dp[i-j][p-1][j] + dp[i][p][j-1]

    解析一下吧:

    当没用到当前最大值j时,因为要划分的数和要划分的数的个数都不变,

    当用到当前最大值j时,因为用到了一个数,所以划分数的总个数减一,并且划分数要减j

    初始化可以是先置零,再dp[i][1][j] = 1,且当j>i时,dp[i][p][j] = dp[i][p][i]

    比如例子:

    dp[5][2][5] = dp[0][1][5]+dp[5][2][4] = 0+dp[1][1][4]+dp[5][2][3] = 1+dp[5][2][3]

    = 1+dp[2][1][3] = 2,得到结果

     

    第四行:

    将n划分成若干奇正整数之和的划分数。同样根据一三行的方法做,令dp[i][j]表示当前的划

    分数为i,最大值为j时的中的划分数,则状态转移方程为

    dp[i][j] = dp[i][i]      if(j%2==1&&j>i)

    = dp[i][i-1]   if(j%2==0&&j>i)

    = dp[i-j][j]+dp[i][j-2]

    解析一下:

    当j>i时没什么好说的了,因为最大数不可能为偶数嘛,

    当j<=i时,如果用到当前最大值,则划分数要减掉当前的j值,

    如果没用到j时,则划分数不变,划分的最大值要减少2

     

    第五行:

    将n划分成若干不同整数之和的划分数。其实这个的状态转移方程挺好找的,

    同样根据一三的方法就行,dp[i][j] = dp[i][j-1]+dp[i-j][j-1],

    解析一下:

    i记录的是要划分的数,j表示当前最大划分到的数中的最大值,

    当用到当前的j时,划分数i要减掉j,并且因为各个划分数不同,所以j还要减掉1;

    当没用到j时,j减一,划分数i不变

     

    由此看来,第一行和第三行的代码很重要,其他的都是可以从它得到啊。。。

    #include <iostream>
    #include <cstring>
    using namespace std;
    #define X 52
    int dp[X][X],dp2[X][X][X],dp3[X][X],dp4[X][X],n,k;
    void f_1_3()
    {
    	for(int i=1;i<X;i++)  //初始化
    		dp[i][0] = 0;
    	dp[0][0] = 1;
    	for(int i=0;i<X;i++)  //实现状态转移方程
    		for(int j=1;j<X;j++)
    			if(i>=j)
    				dp[i][j] = dp[i-j][j]+dp[i][j-1];
    			else
    				dp[i][j] = dp[i][i];
    }
    void f_2()
    {
    	memset(dp2,0,sizeof(dp2));
    	for(int i=1;i<X;i++)  //初始化
    		dp2[i][1][i] = 1;
    	for(int i=0;i<X;i++)  //初始化
    		for(int j=0;j<X;j++)
    			if(j>i)
    				dp2[i][1][j] = dp2[i][1][i];
    			
    			for(int i=1;i<X;i++)  //状态转移
    				for(int p=2;p<X;p++)
    					for(int j=1;j<X;j++)
    						if(j>i)
    							dp2[i][p][j] = dp2[i][p][i];
    						else
    							dp2[i][p][j] = dp2[i-j][p-1][j]+dp2[i][p][j-1];
    }
    void f_4()
    {
    	memset(dp3,0,sizeof(dp3));
    	for(int i=1;i<X;i++)  //初始化,当最大值为1时,只能由i自己本身组成,划分数为1
    		dp3[i][1] = 1;
    	for(int i=1;i<X;i+=2) //涉及到后面的状态转移时i会减少到0,但实际上,当j为奇数时,必须得加1
    		dp3[0][i] = 1;
    	dp3[0][0] = 1;        //初始化1
    	for(int i=1;i<X;i++)  //实现状态转移方程
    		for(int j=3;j<X;j+=2)
    		{
    			if(j>i)
    			{
    				if(i%2)
    					dp3[i][j] = dp3[i][i];
    				else
    					dp3[i][j] = dp3[i][i-1];
    			}
    			else
    				dp3[i][j] = dp3[i-j][j]+dp3[i][j-2];
    		}
    }
    void f_5()
    {
    	memset(dp4,0,sizeof(dp4));
    	for(int i=1;i<X;i++)  //初始化
    	{
    		dp4[1][i] = 1;
    		dp4[0][i] = 1;
    	}
    	for(int i=2;i<X;i++)  //状态转移方程
    		for(int j=1;j<X;j++)
    			if(i<j)
    				dp4[i][j] = dp4[i][i];
    			else
    				dp4[i][j] = dp4[i][j-1]+dp4[i-j][j-1];
    }
    int main()
    {
    	freopen("sum.in","r",stdin);
    	freopen("sum.out","w",stdout);
    	f_1_3();
    	f_2();
    	f_4();
    	f_5();
    	while(cin>>n>>k)
    	{
    		cout<<dp[n][n]<<endl;
    		cout<<dp2[n][k][n]<<endl;
    		cout<<dp[n][k]<<endl;
    		if(n%2)
    			cout<<dp3[n][n]<<endl;
    		else
    			cout<<dp3[n][n-1]<<endl;
    		cout<<dp4[n][n]<<endl;
    		cout<<endl;
    	}
    	return 0;
    }
    
    #include "stdio.h"
    #include "string.h"
    int n;
    int a[130][130];
    void judge()
    {int i,j;
    memset(a,0,sizeof(a));
    for(i=0;i<121;i++)
    {a[i][0]=1;
    a[0][i]=1;
    a[1][i]=1;
    a[i][1]=1;
    }
    for(i=2;i<121;i++)
    for(j=2;j<121;j++)
    if(i>=j)
    a[i][j]=a[i-j][j]+a[i][j-1];
    else
    a[i][j]=a[i][i];
    }
    int main()
    {
        judge();
        for(;scanf("%d",&n)!=EOF;)
    		printf("%d\n",a[n][n]);
        return 1;
    }


  • 相关阅读:
    Java高级特性 第11节 JUnit 3.x和JUnit 4.x测试框架
    Java高级特性 第10节 IDEA和Eclipse整合JUnit测试框架
    Java高级特性 第9节 Socket机制
    Java面向对象和高级特性 项目实战(一)
    Java高级特性 第8节 网络编程技术
    Java高级特性 第7节 多线程
    二十一、字符串类的创建
    二十二、经典问题解析二
    二十一、C++中的临时对象
    二十、对象的销毁
  • 原文地址:https://www.cnblogs.com/yyf573462811/p/6365375.html
Copyright © 2011-2022 走看看