放苹果
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 30796 | Accepted: 19443 |
Description
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。
Input
第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。
Output
对输入的每组数据M和N,用一行输出相应的K。
Sample Input
1
7 3
Sample Output
8
数据分析:
当n = 7, m = 3时,有以下几类分法:
0 0 7 0 1 6 0 2 5 0 3 4 1 1 5 1 2 4 1 3 3 2 2 3
题目分析:
这里需要注意两个同样,盘子是相同的且苹果也相同。所以说随意调换盘子和苹果的位置是可以的。
题目抽象出来的数学模型就是把一个数n拆成m份,使得啊 a1 + a2……am = n,0<=ai<=n
接下来我们看下数据规模,1<=m,n<=10,很小,暴力搜索可行!
算法分析:
题目给出了苹果和盘子全相同,所以说我们这里为了让题目更简单,假设让ai<=a ,正如上面的例子所看到的一样。
搜索时我们需要设置一个条件,就是当苹果到第i个盘子的时候,必训要求第i-1个盘子中的盘子小于第i个盘子,如果大于我们继续加1,直到第i个盘子放置的苹果大于等于第i-1个盘子中的苹果。如果剩下的苹果已经小于前一个盘子中的苹果,我们停止这个分支的搜索。
算法分析二:
临界条件的设置:
搜索不是无穷的搜索下去,他必须有个条件来约束它,这个题目中,当m个盘子都放满苹果时我们结束搜索。
初始赋值与递归运算:
当程序开始时,我们已经将0个苹果放置到一号盘子,递归运算的时候,我们将循环值从0到n,这样子就可以让每个盘子都能放置0到n个苹果,即便它不符合要求。
void dfs(long k, long w){ //初始k=1,w=0;k表示现在已用几个盘子,w表示已经放了几个苹果
long i,u;
if(k == m){ //当最后一个盘子被放置苹果的时候我们进行判断
if(n-w >= s[k-1]){
s[k] = n - w;
z = z + 1;
}
return;
}
for(i = 0; i <= n; i++){
if(i >= s[k-1]){ //如果当前放置的苹果个数大于前一个盘子继续放置
w = w + i;
s[k] = i;
k = k + 1;
dfs(k , w);
w = w - i;
k = k - 1;
}
}
}
此算法效率比较低,为指数级算法。效率相当之低下,不过应对10以内的数据完全能达到要求。
下面我们看一个剪枝。当剩下的苹果平均放到剩余的盘子中的时候每一个盘子分的苹果数目小于前一个盘子放置的苹果数目,这样就不满足我们给的要求a(i-1) < a(i),所以我们对它进行剪枝。下面是优化后的程序。
void dfs(long k,long w){
long i,u;
if(k == m){
if(n-w >= s[k-1]){
s[k] = n - w;
z = z + 1;
}
return;
}
for(i = 0; i < n; i++){
if((i >= s[k-1])&&((n-w)/(m-k)>=s[k-1])){
w = w + i;
s[k] = i;
k = k + 1;
dfs(k,w);
w = w - i;
k = k - 1;
}
}
}
暴力搜索只能在很小的数据范围内使用,一般情况下,所谓的暴力也就是枚举了所有的状态,在这些状态中找到最终的解。在很多的情况下,搜索有以下几类:
1、 求所有状态的个数,例如放苹果。
2、 找出所有解的状态,例如把放苹果的每种方式求出来。
3、 求出一个可行解。
4、 求出所有可行解的最优解。
搜索题目小结:
搜索题目无外乎上面所说的几类,接下来我们来一一分析下他们的特点。
求最终状态的个数:这个问题有时可以转化为一个组合问题。
求出所有最终状态:这个问题是最麻烦,必须对状态空间进行全遍历。
求出一种可行状态:这个相对简单,有时候可以用随机化或者搜索到其中一个解的时候停止。