具有1D阵列的动态编程USACO训练:子集总和

时间:2016-12-15 05:19:00

标签: c++ algorithm dynamic knapsack-problem

在解决USACO培训问题时,我发现了动态编程。处理这个概念的第一个训练问题是一个叫做子集和的问题。

问题陈述遵循

对于从1到N的多组连续整数(1 <= N <= 39),可以将该组分成两组,其总和相同。 例如,如果N = 3,则可以以一种方式对集合{1,2,3}进行分区,以使两个子集的总和相同:

{3}和{1,2}

这计为单个分区(即,将顺序颠倒计为相同的分区,因此不会增加分区的数量)。 如果N = 7,有四种方法可以对集合{1,2,3,... 7}进行分区,以便每个分区具有相同的总和:

{1,6,7}和{2,3,4,5}

{2,5,7}和{1,3,4,6}

{3,4,7}和{1,2,5,6}

{1,2,4,7}和{3,5,6}

给定N,你的程序应该打印包含从1到N的整数的集合的方式的数量可以被分成两个总和相同的集合。如果没有这样的方法,请打印0。 您的程序必须计算答案,而不是从表中查找。

输入格式 输入文件包含一行,其中一个整数表示N,如上所述。

SAMPLE INPUT(文件subset.in) 7

输出格式 输出文件包含一个带有单个整数的行,该整数表示可以从集合{1,2,...,N}中生成多少个相同和的分区。如果没有方法进行相同和分区,则输出文件应包含0。 SAMPLE OUTPUT(文件subset.out) 4

经过多次阅读后,我发现了一种被解释为 0/1背包问题的变体的算法。我在我的代码中实现了它,我解决了这个问题。但是,我不知道我的代码是如何工作的或者是怎么回事。

*主要问题:我想知道是否有人可以向我解释背包算法是如何工作的,以及我的程序如何在我的代码中实现这一点?

我的代码:

 #include <iostream>
 #include <fstream>
 using namespace std;
 int main()
 {
      ifstream fin("subset.in");
      ofstream fout("subset.out");
      long long num=0, ways[800]={0};
      ways[0]=1;
      cin >> num;

      if(((num*(num+1))/2)%2 == 1)
      {
           fout << "0" << endl;
           return 0;
      }
      //THIS IS THE BLOCK OF CODE THAT IS SUPPOSED TO BE DERIVED FROM THE 
      //   O/1 KNAPSACK PROBLEM
      for (int i = 1; i <= num; i++) 
      {
           for (int j = (num*(num+1))/2 - i; j >= 0; --j) 
           {
                ways[j + i] += ways[j];
           }
      }
      fout << ways[(num*(num+1))/2/2]/2 << endl;
      return 0;
 }

*注意:请注意,这段代码确实有用,我只想解释它为什么有效。谢谢:)

1 个答案:

答案 0 :(得分:1)

我想知道为什么众多消息来源无法帮助你。

用我丑陋的英语再试一次:

  

方式[0] = 1;

有一种方法可以赚空钱

  

NUM *(NUM + 1))/ 2

这是MaxSum - 范围1..num中所有数字的总和(算术级数之和)

  

if((num *(num + 1))/ 2)%2 == 1)

没有机会将奇数值分成两个相等的部分

  

for(int i = 1; i&lt; = num; i ++)

对于范围内的每个数字

  

for(int j =(num *(num + 1))/ 2 - i; j&gt; = 0; --j)    方式[j + i] + =方式[j];

可以使用总和j + i和值为j的项构建总和i

例如,考虑你想要和15 在外循环的第一步,你使用的是数字1,并且有ways[14]种变体可以得到这个总和 在外循环的第二步,您使用数字2,并且有ways[13] 新的变体来计算此总和,您必须添加这些新变体。
在外循环的第三步,您使用数字3,并且有ways[12] 新的变体来计算此总和,您必须添加这些新变体。

  

方式[(NUM *(NUM + 1))/ 2/2] / 2

输出MaxSum / 2的方法数,除以2以排除对称变量([1,4] + [2,3] / [2,3] + [1,4])

自我思考的问题:为什么内循环反向?