带一维数组 USACO 培训的动态规划:子集和

Dynamic Programming w/ 1D array USACO Training: Subset Sums

在解决 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。 您的程序必须计算答案,而不是从 table 中查找答案。

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

样本输入(文件subset.in) 7

输出格式 输出文件包含一行和一个整数,表示可以从集合 {1, 2, ..., N} 中创建多少个相同和的分区。如果无法创建相同和的分区,则输出文件应包含 0。 示例输出(文件 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;
 }

*注意:只是强调一下,这段代码确实有效,我只是想解释一下为什么它有效。谢谢:)

我想知道为什么很多资源都无法帮助您。

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

ways[0]=1;

只有一种方法可以生成空总和

num*(num+1))/2

这是 MaxSum - 1..num 范围内所有数字的总和(等差级数的总和)

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

没有机会将奇数分成两等份

for (int i = 1; i <= num; i++)

对于范围内的每个数字

for (int j = (num*(num+1))/2 - i; j >= 0; --j) ways[j + i] += ways[j];

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

例如,假设您要求和 15。
在外循环的第一步,您使用的是数字 1,并且有 ways[14] 个变体来计算这个总和。
在外循环的第二步,你使用的是数字 2,并且有 ways[13] new 个变体来计算这个和,你必须添加这些新变体。
在外循环的第三步,你使用的是数字 3,并且有 ways[12] new 变体来计算这个和,你必须添加这些新变体。

ways[(num*(num+1))/2/2]/2

输出方式数MaxSum/2,除以二排除对称变体([1,4]+[2,3]/[2,3]+[1,4])

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