放苹果问题集锦(每个盘子至少放1个苹果)

/*
	Name: 放苹果问题集锦(每个盘子至少放1个苹果) 
	Copyright: 
	Author: 
	Date: 31-07-17 21:22
	Description: 放苹果
查看 提交 统计 提问
总时间限制: 1000ms 内存限制: 65536kB
描述
把N个同样的苹果放在M个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。
输入
第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。
输出
对输入的每组数据N和M,用一行输出相应的K。
样例输入
1
7 3
样例输出
4

算法思路:
回溯算法:有2种思路,一种是确保后面的盘子中苹果不比前面的少,另一种是确保后面的盘子中苹果不比前面的多。  
第2种思路递归深度较少,但代码复杂些,特别要注意第n个盘子能放苹果的数量范围。   
基本上递归问题都可以转化为记忆化搜索,然后转换为动态规划问题。 
回溯算法相当于穷举,不但可以获得组合的数量,还可以存储具体的解空间。
记忆化搜索和动态规划算法均采用了确保后面的盘子中苹果不比前面的多的思路;其中动态规划进行了降维优化。 
每个盘子至少放1个苹果和每个盘子至少放0个苹果的递归表达式以及边界条件都不一样。 
*/
#include<iostream>

using namespace std;
               
const int MAXN = 10; //苹果的最大数量 
const int MAXM = 10; //盘子的最大个数
int A1[MAXN+1], A2[MAXN+1]; 
int M, N, s1, s2; 
long long B[MAXM+1][MAXN+1]; //记录给定n个盘子装m个苹果的方案总数 
long long B2[MAXM+1][MAXN+1]; //记录给定n个盘子装m个苹果的方案总数 
long long pre[MAXN+1]; //记录给定n个盘子装m个苹果的方案总数 
long long cur[MAXN+1]; //记录给定n个盘子装m个苹果的方案总数 
long long F[MAXN+1]; //记录给定n个盘子装m个苹果的方案总数 

void DFS_1(int k, int n); //n表示共n个苹果,k表示第k个盘子   
void DFS_2(int k, int n); //n表示共n个苹果,k表示第k个盘子
long long Fun(int k, int n); //记忆化搜索
void Fun2(int k, int n); //动态规划:使用二维数组 
long long Fun3(int k, int n); //动态规划:使用2个一维数组 
long long Fun4(int k, int n); //动态规划:使用1个一维数组 

int main() 
{
	int t;
	 
	Fun2(MAXM, MAXN); 
	cin >> t;      
    for (int k=0; k<t; k++)      
    {      
        cin >> N >> M;      
             
		s1 = 0;     
        DFS_1(1, N);      
        cout << s1 << endl;    
        
        s2 = 0;     
        int minNum = N/M + (N%M!=0);     
        for (A2[1]=N-M+1; A2[1]>=minNum; A2[1]--)//第一个盘中放A2[1]个苹果     
        {      
            DFS_2(2, N-A2[1]);      
        }    
        cout << s2 << endl; 
        
		cout << Fun(M, N) << endl; 
		cout << B2[M][N] << endl; 
		cout << Fun3(M, N) << endl; 
    }      
	
	return 0;
}

void DFS_1(int k, int n) //n表示共n个苹果,k表示第k个盘子       
{      
    if (k == M)//最后一个盘子      
    {      
        A1[k] = n;      
        cout << s1 << " : ";      
        for (int i=1; i<=k; i++)      
            cout << A1[i] << " ";      
        cout << endl;      
        s1++;      
    }      
    else  //至少还有2个盘子    
    {      
        for (A1[k]=max(1,A1[k-1]); A1[k]+A1[k]<=n; A1[k]++)      
        {   //确保剩下的苹果不比当前盘子中的少       
            DFS_1(k+1, n-A1[k]);   
        }      
    }         
}     

void DFS_2(int k, int n) //n表示共n个苹果,k表示第k个盘子   
{      
	if (n <= 0)
		return;
		
    if (k == M)//最后一个盘子 
    {     
		if (n > A2[k-1]) //确保后面的盘子中苹果不比前面的多  
			return;
        A2[k] = n;    
        cout << s2 << " : ";      
        for (int i=1; i<=k; i++)     
            cout << A2[i] << " ";  
        cout << endl;      
        s2++;      
    }      
    else      
    {   
        int maxNum = min(n, A2[k-1]);//确保后面的盘子中苹果不比前面的多 
        for (A2[k]=maxNum; A2[k]>=1; A2[k]--)       
        {           
			DFS_2(k+1, n-A2[k]);       
        }      
    }         
} 

long long Fun(int k, int n) //记忆化搜索
{   
	if (B[k][n] != 0)
		return B[k][n]; 
	if (k == 1 || n == k)//只有1个盘子或者盘子与苹果数量相等 
		B[k][n] = 1;
	else if (n < k) //苹果数量少于盘子 
		B[k][n] = 0;
	else //两种情况:第k个盘子只放一个苹果;每个盘子至少放2个苹果,则每个盘子中拿走1个苹果后,分配方法数量不变 
		B[k][n] = Fun(k-1, n-1) + Fun(k, n-k);
    
    return B[k][n];
}  

void Fun2(int k, int n) //动态规划:使用二维数组 
{   
	for (int j=1; j<=n; j++)//j个苹果放到1个盘子里 
		B2[1][j] = 1; 
    for (int i=2; i<=k; i++) 
    {  
        for (int j=i; j<=n; j++) 
        {  
            B2[i][j] = B2[i-1][j-1] + B2[i][j-i];  
        }  
    }  
}  

long long Fun3(int k, int n) //动态规划:使用2个一维数组 
{   
	for (int j=1; j<=n; j++)//j个苹果放到1个盘子里 
		pre[j] = 1; 
    for (int i=2; i<=k; i++) 
    {  
		for (int j=1; j<i; j++) //苹果数量少于盘子 
        {  
            cur[j] = 0;  
        }  
        for (int j=i; j<=n; j++) 
        {  
            cur[j] = pre[j-1] + cur[j-i];  
        }  
        for (int j=1; j<=n; j++) 
        {  
            pre[j] = cur[j];  
        }  
    }  
    
    return pre[n];
}    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值