带一维数组的动态编程 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 培训:子集总和的主要内容,如果未能解决你的问题,请参考以下文章