带一维数组的动态编程 USACO 培训:子集总和

Posted

技术标签:

【中文标题】带一维数组的动态编程 USACO 培训:子集总和【英文标题】:Dynamic Programming w/ 1D array USACO Training: Subset Sums 【发布时间】:2016-12-15 05:19:00 【问题描述】:

在解决 USACO 培训问题时,我发现了动态规划。处理这个概念的第一个训练问题是一个称为子集和的问题。

问题陈述如下


对于从 1 到 N (1

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 的整数,如上所述。

样本输入(文件子集.in) 7

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

只有一种方法可以使总和为空

num*(num+1))/2

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

如果(((num*(num+1))/2)%2 == 1)

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

for (int i = 1; i

对于范围内的每个数字

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

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

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

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

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

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

【讨论】:

以上是关于带一维数组的动态编程 USACO 培训:子集总和的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的子集总和方法不正确?

为什么我的子集总和方法不正确?

打印总和等于 k ​​的集合的子集

总和小于 M 的大小为 K 的子集的最大总和

创建一维数组,其中元素是存储函数的二维 numpy 数组的总和

698. 划分为k个相等的子集(Python)