海盗分金币问题

今天晚上,有个同学出了道海盗分金币的问题来拷问我。我干脆用程序来把它做了。唉,好吧,承认概率已经退步了,而且还很不爱动脑筋。不过如果让计算机来处理这样的问题,不考虑算法复杂度的话,实在忒简单了。不考虑算法复杂度,几分钟就把程序写完跑通了。可是更高效的程序,和直接的概率解法是怎样的呢?下次请教这位大牛同学吧。

不废话了,先给题:3名海盗分15枚金币和15枚银币,每人最多只能拿12枚钱币,而且必须分完,问总共有多少种不同的分法?

还是不废话了,直接上代码(再次声明,不考虑算法复杂度啊!)

#include <iostream>

struct Pirate {
    int gold;
    int silver;
};

int main()
{
    Pirate a, b, c;
    int gold_num = 15, silver_num = 15;
    int counter = 0;
    
    for (int i = gold_num; i >= 0; i--) {
        for (int j = silver_num; j >= 0; j--) {
            a.gold = i;
            a.silver = j;
            if (a.gold + a.silver > 12 || a.gold + a.silver == 0) continue;
            
            for (int k = gold_num-i; k >= 0; k--) {
                for (int l = silver_num-j; l >= 0; l--)
                {
                    b.gold = k;
                    b.silver = l;
                    if (b.gold + b.silver > 12 || b.gold + b.silver == 0) continue;
                    
                    c.gold = 15 - i - k;
                    c.silver = 15 - j - l;
                    if (c.gold + c.silver > 12 || c.gold + c.silver == 0) continue;
                    
                    counter ++;
                    std::cout << "a.gold=" << a.gold << ", a.silver=" << a.silver << ",b.gold=" << b.gold << ", b.silver="  << b.silver << ",c.gold=" << c.gold << ", c.silver=" << c.silver << std::endl;
                }
            }
        }
    }
    std::cout << "counter = " << counter << std::endl; 
    return 0;
}

最后的答案是2422. 

换个Python版的程序:

def main():
    gold_num, silver_num = 15, 15
    limit = 12
    counter = 0

    for i in range(gold_num, -1, -1):
        for j in range(silver_num, -1, -1):
            if i+j > limit or i+j == 0:
                continue 
            
            for m in range(gold_num-i, -1, -1):
                for n in range(silver_num-j, -1, -1):
                    if m+n > limit or i+j == 0:
                        continue
                        
                    c1, c2 = gold_num-i-m, silver_num-j-n
                    if c1+c2 > limit or c1+c2 == 0:
                        continue
                        
                    counter += 1
                    
                    print "a.gold=%d, a.silver=%d, " \
                          "b.gold=%d, b.silver=%d, " \
                          "c.gold=%d, c.silver=%d" \
                          % (i, j, m, n, c1, c2)
                    
    print "Counter = %d" % counter                

if __name__ == "__main__":
    main()
    

最后补充一下,后来和金融大牛同学联系过后,发现人家是也是写了个程序做的。

如果您有更好的解法,不妨留言。


【后记】2016-06-19更新

鉴于4重for循环的程序较大束缚了因题目变化而引起的程序不变性,比如海盗变为10个,钱币变为10种,因此,本程序其实可改用递归实现。递归的实现可以较好地适应海盗数目的变化,依然不便于适应钱币种类的变化。那么算法复杂度有变化吗?没有。递归的算法复杂度和上述4重for循环的算法复杂度是一样的。除了理论分析,还可以通过添加一个loop_counter变量来统计最内部循环体被执行的次数来验证。

#include <iostream>
#include <sstream>


struct Pirate {
    int gold;
    int silver;
};


const int limit = 12;
const int totalGold = 15;
const int totalSiver = 15;
const int totalPirates = 3;


Pirate * pList = new Pirate[totalPirates];


int globalIndex = 0;


int Resolve(int leftPirates, int leftGold, int leftSilver)
{
    if (leftPirates < 1) return 0;
    
    if (leftPirates == 1) {
        if (leftGold + leftSilver > limit || leftGold + leftSilver == 0) {
            return 0;
        } else {
            int index = totalPirates - leftPirates;
            pList[index].gold = leftGold;
            pList[index].silver = leftSilver;
            
            ++globalIndex;
            std::ostringstream strm;
            strm << globalIndex << ": "; 
            for (int count = 0; count < totalPirates; ++count) {
                strm << "<" << count+1 << ">" << pList[count].gold << ", " << pList[count].silver << " ";
            }
            strm << "\n";
            std::cout << strm.str();
            
            return 1;
        }
    }
    
    int sum = 0;
    for (int i = 0; i <= leftGold; ++i)
        for (int j = 0; j <= leftSilver; ++j) { 
            if (i + j == 0 || i + j > limit) continue;  // loop_counter can be added before this line
            int index = totalPirates - leftPirates;
            pList[index].gold = i;
            pList[index].silver = j;
            sum += Resolve(leftPirates - 1, leftGold - i, leftSilver - j);
        }
    return sum;
}


int main() 
{
    int sum = Resolve(totalPirates, totalGold, totalSiver);
    std::cout << "sum = " << sum << std::endl;
    delete [] pList;
}

最后,分享一点有趣的数据:

如果改成金币20枚银币20枚,5个海盗,每个最多取16枚,最少取1枚,那么:

总共分法为:67,695,246种,循环体执行次数为:85,254,578次,计算机耗时约:1.2秒。

如果改成金币20枚银币20枚,6个海盗,每个最多取16枚,最少取1枚,那么:

总共分法为:1,847,553,264种,循环体执行次数为:2,196,020,339次,计算机耗时约:28秒。


(完)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值