父亲,两个儿子,999幅画

时间:2014-10-02 01:00:44

标签: c# .net algorithm math

这是一份工作申请的问题: “一位父亲有两个儿子和999幅画作。每幅画都有不同的价值:第一幅值1,第二幅值2,依此类推,直到最终的画作价值999.他想将他的所有画作分成两部分。儿子们,每个儿子都有同等的价值。有999幅画有多少种方式可以做到这一点? 例如:如果父亲有7幅画作,他可以​​通过给第一个儿子画作1,6和7来公平地划分它们。第二个儿子将获得2,3,4和5.两者的总和相等,14。有7幅画,父亲可以用4种方式对它们进行相当划分(其他3种不在此列出),所以解决方案是4。 提示:数字可能很大,请将最后10位数字和解决方案草图发送给我们。“

我所做的是尝试使用暴力方法,通过编写一个c#程序将所有可能的组合加起来,该程序在循环内编写自己的c#程序,如下所示:

StringBuilder sb = new StringBuilder();
for (short i = 2; i <= 999; i++) //starts from 2 because 1 is always added to the total for one side
{
    sb.AppendLine("for (byte i" + i.ToString() + " = 0; i" + i.ToString() + " < 2; i" + i.ToString() + "++)");
    sb.AppendLine("{");
}

for (int i = 2; i <= 999; i++)
{
    sb.Append("if (i" + i.ToString() + " == 1) { total += " + i.ToString() + "; }\n");
}

for (short i = 2; i <= 999; i++)
{
    sb.AppendLine("}");
}

然后在结果中的if块之后添加:

if (total == 249750)
{
    count++; //count is a BigInteger
}
total = 1;

这种方法在技术上应该起作用(在较少数量的绘画上进行测试),但问题是它是一个HUUUGE数字,用这种方式在我的计算机上计算结果需要一百万年左右......在合理的时间内是否有一些数学技巧可以做到这一点?

2 个答案:

答案 0 :(得分:9)

更容易理解确定第一个儿子可以获得多少种值k的方式,其中k是一个参数。找出适当的概括是一门艺术;它在名为动态编程的算法课程中讲授。

x成为变量。所需的数学见解是,对于n绘画,多项式乘积中x^k的系数

x (1 + x^2) (1 + x^3) ... (1 + x^n)
x中的

是第一个儿子获得价值k的方式的数量(包括值1的绘画)。这是因为该产品分发到

(sum for i_2 = 0 to 1) (sum for i_3 = 0 to 1) ... (sum for i_n = 0 to 1)
    x^(1 + 2 i_2 + 3 i_3 + ... + n i_n),

这实际上是您的强力解决方案评估此产品的方式。这里的动态程序相当于逐个分配因子,而不是一次分配所有因子,例如,

x (1 + x^2) = x + x^3
x (1 + x^2) (1 + x^3) = (x + x^3) (1 + x^3)
                      = x + x^3 + x^4 + x^6.
x (1 + x^2) (1 + x^3) (1 + x^4) = (x + x^3 + x^4 + x^6) (1 + x^3)
                                = x + x^3 + x^4 + x^6 + x^4 + x^6 + x^7 + x^9
                                = x + x^3 + 2 x^4 + 2 x^6 + x^7 + x^9.

节省时间来自重复条款。我们已经只有​​六个任期而不是八个(两个到三个权力)。

仅保留最后十位数意味着我们可以在模数为10^10的整数环中评估此产品。因此,我们可以减少以该数为模的中间系数,以避免求助于bignums。这个技巧是竞争性编程社区所熟知的,在抽象代数或数论的课程中正式涵盖。

Mathematica中:

In[1]:= Coefficient[x Product[1+x^i,{i,2,7}],x^(Sum[i,{i,1,7}]/2)]

Out[1]= 4

In[2]:= Coefficient[x Product[1+x^i,{i,2,8}],x^(Sum[i,{i,1,8}]/2)]

Out[2]= 7

在Java中:

public class PartitionPaintings {
    public static void main(String[] args) {
        long[] p = new long[] {0, 1};
        for (int i = 2; i <= Integer.parseInt(args[0]); i++) {
            long[] q = new long[p.length + i];
            for (int k = 0; k <= p.length - 1; k++) {
                for (int j = 0; j <= 1; j++) {
                    q[k + i * j] = (q[k + i * j] + p[k]) % 10000000000L;
                }
            }
            p = q;
        }
        System.out.println(p[(p.length - 1) / 2]);
    }
}

答案 1 :(得分:1)

这是一项数学工作。

您基本上在寻找一个number partition,一个distinct parts

所有绘画的总价值是整数1 ... 999的总和。 (n * (n+1)) / 2 according to Gauss,我们得到:(999 * 1000) / 2 = 499500。因此,每个儿子都应该获得总价值为249750的绘画作品。

现在,我们只需要找到该值的数字分区,其中不同的部分不超过999.我们将每个分区分配为一个儿子的绘画集,第二个儿子将获得剩余的绘画(具有相同的总价值。)

因此,唯一棘手的部分是找出不同和有界部分的分区函数。但我想你也可以通过编程方式做到这一点。

来自德黑兰谢里夫科技大学的Mohammadreza Bidar实际上写了一篇文章,其中有一个很有希望的标题“将整数划分为独特的有界部分,身份和边界”。您可以在INTEGERS, volume 12中阅读(这是第8篇文章)。