| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 792 人关注过本帖
标题:向beyondyf大哥请教,并和大家一起研究。
只看楼主 加入收藏
有容就大
Rank: 16Rank: 16Rank: 16Rank: 16
来 自:东土大唐
等 级:版主
威 望:74
帖 子:9048
专家分:14309
注 册:2011-11-11
结帖率:99.34%
收藏
已结贴  问题点数:20 回复次数:10 
向beyondyf大哥请教,并和大家一起研究。
下面是我给出的一道题:
大家一起来做题:二维数组取数求和。给力--->给分!
输入一个n行m列(2 <= n <= 16 , 2 <= m <= 24)二维数组,每行的元素值都是0 1 2 3 …… m -1;要求从每一行取一个元素,使取得的n个元素的和等于m-1.问有多少种取法,并打印出每种取法对应的n个元素。

比如 3 * 4 的二维数组a   0 1 2 3
                         0 1 2 3
                         0 1 2 3
取数使a[0][?] + a[1][?] + a[2][?] = 3 有多少种取法,及其对应的3个数。取法数太大的可以不用打印对应元素。
连接是:https://bbs.bccn.net/thread-359913-1-1.html

杨大哥给了代码我贴上来,并请教几个问题(为了测试数据,我改了下范围和循环,核心没动)。
程序代码:
#include 
#include 
#define N_MAX        100
#define M_MAX        100

__int64 a[N_MAX + 1][M_MAX];

void init()
{
        int i, j;
        for(i = 1; i <= N_MAX; i++)
        for(j = 1; j < M_MAX; j++)
                a[i][j] = a[i][j - 1] + a[i - 1][j] + 1;
        for(i = N_MAX; i; i--)
        for(j = M_MAX - 1; j; j--)
                a[i][j] -= a[i][j - 1];
}

__int64 cal(int n, int m)
{
        if(n <= 0 || m <= 0) return 0;
        if(n == 1) return m;
        if(m == 1) return n;
        return cal(n, m - 1) + cal(n - 1, m) + 1;
}

__int64 c(int n, int m)
{
        __int64 r;
        int i;
        for(r = i = 1; i < n; i++)
        {
                r *= m - 1 + i;
                r /= i;
        }
        return r;
}

void print(int n, int m, int index)
{
        int i;
        static int list[N_MAX];
        if(n <= 1)
        {
                for(i = 0; i < index; printf("%d ", list[i++]));
                printf("%d\n", m);
                return;
        }
        for(list[index] = i = 0; i <= m; list[index] = ++i)
                print(n - 1, m - i, index + 1);
}

int main()
{
        int n, m;
        int start, end;

        while(1)
        {
          init();
          printf("Please input n & m:");
          scanf("%d %d", &n, &m);         
          if(a[n][m - 1] <= 10000) print(n, m - 1, 0); else printf("it's too large.\n");
          printf("The methods count is %I64d\n", a[n][m - 1]);
          printf("The mothods count is %I64d\n", c(n, m));
          if (n <= 16 && m <= 21)
          {
            start = clock();
            printf("The methods count is %I64d\n", cal(n, m - 1) - cal(n, m - 2));
            end = clock();
            printf("time is :%-3dsecond!\n",(end - start)/CLK_TCK);
          }
        }
    return 0;
}
上面题供了3种放法.一时难以领会,特别是打印函数,print(n, m - 1, 0); 为什么index传入的是0,而在整个
print函数中index自身貌似没发生变化,那么for(i = 0; i < index; printf("%d ", list[i++]));这个句能循环起来吗?
下面是我的测试数据:
图片附件: 游客没有浏览图片的权限,请 登录注册
图片附件: 游客没有浏览图片的权限,请 登录注册

为什么输入1 1时3个函数中有个不一样,输入到 32 32时数学方法应该是正确的吧,变成负数说明数太大装不下了,而第一种方法还能显出一个整数?
搜索更多相关主题的帖子: 大哥 元素 
2012-02-03 13:21
beyondyf
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:103
帖 子:3282
专家分:12654
注 册:2008-1-21
收藏
得分:16 
因为你的题目中定义范围m是大于等于2的,所以我的代码中便没有考虑m=1的情况,或者说定义m = 1时的值为0。
init中是没有初始化第0列(值为0),cal中则为if(n <= 0 || m <= 0) return 0;
而c中则是以组合公式计算的。其根源是0的阶乘为多少。目前,我们的规定是0!=1,我对它的理解只停留在为了计算方便的层面,具体它的数学或物理意义是什么我也不理解。

如果要让m可以等于1的话,那我个人觉得n行1列只能是n个0,应该算作有1种取法。c和print的输出是合理的,init和cal也只需要简单修改一下m = 0(ps:函数参数中的m与题目中的m意义不同,相当于题目中的m - 1)的定义。

print函数是通过递归来计算每组输出的每个元素值的。index的意义是某一组值的当前计算的元素索引。每次执行print函数只计算一个位置,所以index在其中也不应该改变。发生变化的是递归调用的下一个位置print(n - 1, m - i, index + 1);
这里请注意index与n的关系,n在不断减小,而index在不断增大,它们的和是初始时的n。当n为1时,表示只剩一行来取数,为了使整体值为m,那它的取值只能是m,这时也表示一种取法结束了,对应每行的取值保存在静态数组list里,而其中所存的元素数量即是index的值。
你的测试结果也说明print确实按预想完成了输出,它确实循环起来了

重剑无锋,大巧不工
2012-02-03 15:25
有容就大
Rank: 16Rank: 16Rank: 16Rank: 16
来 自:东土大唐
等 级:版主
威 望:74
帖 子:9048
专家分:14309
注 册:2011-11-11
收藏
得分:0 
搞清楚点眉目了。
 if(n == 1)
        {
                for(i = 0; i < index; printf("%d ", list[i++]));
                printf("%d\n", m);
                return;
        }
当n==1时index应该是n-1,那么for(i = 0; i < index; printf("%d ", list[i++]));输出的是前面N-1个元素,    也就是输出到LIST[N-2].
printf("%d\n", m);再补上最后一个此时的函数传来的M值,一起就是N个元素,因为一组输出必须是N个元素。

 for(list[index] = i = 0; i <= m; list[index] = ++i)
                print(n - 1, m - i, index + 1);
从0到m(主函数开始传入的是m-1符合题意)每一类都递归调用print,而每一类index的初始值都是0,即LIST[0].他是在递归调用中随着n递减而递增的,当n减到1,index就增加到n-1,此时m通过n-1次(m-i)的变化传入
print(1, m, n-1);中,由printf("%d\n",m);追加打印出来,补齐n个元素。

杨大哥不知道我上面的理解对不对。
发现这个print()函数太神奇了。

图片附件: 游客没有浏览图片的权限,请 登录注册





[ 本帖最后由 有容就大 于 2012-2-4 10:34 编辑 ]

梅尚程荀
马谭杨奚







                                                       
2012-02-04 10:26
beyondyf
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:103
帖 子:3282
专家分:12654
注 册:2008-1-21
收藏
得分:0 
嗯,是这个意思。递归是很具美感的一种数学结构,递归代码往往都简洁漂亮。

重剑无锋,大巧不工
2012-02-04 11:07
有容就大
Rank: 16Rank: 16Rank: 16Rank: 16
来 自:东土大唐
等 级:版主
威 望:74
帖 子:9048
专家分:14309
注 册:2011-11-11
收藏
得分:0 
回复 4楼 beyondyf
杨大哥再请教个问题,即把m个相同的球放到n个不同的盒子中,有多少种放法的问题:
我这么考虑的,第一个位置放盒子,有n种选择,后面的看成n-1个位置和m个不同球的排列。
--> S1 = n * P(n+m-1,n+m-1),但是球的相同的要除以m个球的全排列
--> S2 = S1 / P(m, m),又只考虑后面n-1个盒子的位置排列即可,球放入盒子间隙(包括两头)的
                      排列是重复的,要除以n个间隙的全排列
--> S3 = S2 / P(n, n).
--> S3 = n * (n+m-1)!/ (n!*m!)
       = (n+m-1)!/((n-1)!*m!)
       = C(n+m-1, n-1)
       = C(n+m-1, m)
不知道对不对,还有没有更好的理解方式?

梅尚程荀
马谭杨奚







                                                       
2012-02-04 11:30
vandychan
Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15
等 级:贵宾
威 望:18
帖 子:2296
专家分:6418
注 册:2010-8-20
收藏
得分:2 
大哥就是大哥

到底是“出来混迟早要还”还是“杀人放火金腰带”?
2012-02-04 12:01
beyondyf
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:103
帖 子:3282
专家分:12654
注 册:2008-1-21
收藏
得分:0 
这个用隔板来理解更方便。
比如将m个球排成一列,用0来表示,用1表示盒子的边框,来分割0,那么n个盒子可以用n-1个1来表示
对于每一种放法,会形成 000101011000110这样一个数列。这相当于在n + m - 1个位置选取n - 1个位置放1的放法。

重剑无锋,大巧不工
2012-02-04 12:03
有容就大
Rank: 16Rank: 16Rank: 16Rank: 16
来 自:东土大唐
等 级:版主
威 望:74
帖 子:9048
专家分:14309
注 册:2011-11-11
收藏
得分:0 
回复 7楼 beyondyf
55555看了你的解释我觉得自己悲催了啊

梅尚程荀
马谭杨奚







                                                       
2012-02-04 12:10
beyondyf
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:103
帖 子:3282
专家分:12654
注 册:2008-1-21
收藏
得分:0 
关于组合数学我掌握的也就点皮毛,这方面老杨和小曹(czz5242199)也许比我更强。
你也不必妄自菲薄,能全面掌握所有知识的人也就传说中的那么几个。大家互相交流,取长补短。学而时习之,不亦说乎

重剑无锋,大巧不工
2012-02-04 13:15
zklhp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:china
等 级:贵宾
威 望:254
帖 子:11485
专家分:33241
注 册:2007-7-10
收藏
得分:2 
以下是引用beyondyf在2012-2-4 12:03:06的发言:

这个用隔板来理解更方便。
比如将m个球排成一列,用0来表示,用1表示盒子的边框,来分割0,那么n个盒子可以用n-1个1来表示
对于每一种放法,会形成 000101011000110这样一个数列。这相当于在n + m - 1个位置选取n - 1个位置放1的放法。

这个隔板的方法貌似高中做数学题用过 呵呵
2012-02-04 15:23
快速回复:向beyondyf大哥请教,并和大家一起研究。
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.059746 second(s), 8 queries.
Copyright©2004-2025, BCCN.NET, All Rights Reserved