Distribute numbers to two “containers” and minimize their difference of sum
it can be solved by Dynamical Programming.Here are some useful link:
Tutorial and Code: http://www.cs.cornell.edu/~wdtseng/icpc/notes/dp3.pdf
A practice: http://people.csail.mit.edu/bdean/6.046/dp/ (then click Balanced Partition)
What's more, please note that if the scale of problem is damn large (like you have 5 million numbers etc.), you won't want to use DP which needs a too huge matrix. If this is the case, you want to use a kind of Monte Carlo Algorithm:
1. divide n numbers into two groups randomly (or use your method at this step if you like);
2. choose one number from each group,
3. if (to swap these two number decrease the difference of sum) swap them;
4. repeat step 2 until "no swap occurred for a long time".
You don't want to expect this method could always work out with the best answer, but it is the only way I know to solve this problem at very large scale within reasonable time and memory.
====== 找出一组数字中, 和为整个数组和的一半(或任意其他值)的子集 =======
Dynamic Programming: Partition
假设数组C[]特别大, n>10000这样, 这需要先计算出和, 假设和为N, 创建一个大小为N+1的boolean数组T[], 全部设为false. 在以下的处理中, 会将可以组合出的值, 比如s, 将T[s]置为true, 如果T[N/2]为true, 则出现符合要求的子集.1. 依次将C[i]与之前产生的T[j]=true的点, 组合成为新的和, 并将其下标的T置为true
2. j循环中, 需要从右往左进行, 以免因T赋值的下标出现到j的右侧, 而导致重复计算
bool T[];
bool partition( vector< int > C ) {
// compute the total sum
int n = C.size();
int N = ;
for( int i = ; i < n; i++ ) N += C[i];
// initialize the table
T[] = true;
for( int i = ; i <= N; i++ ) T[i] = false;
// process the numbers one by one
for( int i = ; i < n; i++ )
for( int j = N C[i]; j >= ; j )
if( T[j] ) T[j + C[i]] = true;
return T[N / ];
}
优化:
1. 不需要每次从最右端开始, 记录每次j的最后值
2. 不需要计算到N, 到N/2就可以, 因为有sum=x的话, N-x也是存在的
3. 将C排序, 这样T的true下标增长会从慢->快
bool T[];
bool partition( vector< int > C ) {
// compute the total sum and sort C
int n = C.size();
int N = ;
for( int i = ; i < n; i++ ) N += C[i];
sort( C.begin(), C.end() );
// initialize the table
T[] = true;
for( int i = ; i <= N; i++ ) T[i] = false;
int R = ; // rightmost true entry
// process the numbers one by one
for( int i = ; i < n; i++ ) {
for( int j = R; j >= ; j )
if( T[j] ) T[j + C[i]] = true;
R = min( N / , R + C[i] );
}
return T[N / ];
}
====== 如果数组每个成员的数量是无限的, 成为一个询问对于某个值, 可能的组合数量的问题 =======
比如如何组合出M这个数字.
这时候将j从左往右遍历就行了, 因为这个T下标会不断重复计算而增长同时, T不再是boolean数组, 而是int数组, 初始化为0, 每次命中, 值都增加1, 最后T[M]的值就是组合数量
int T[];
int coins( vector< int > C, int N ) {
// initialize the table
T[] = ;
for( int i = ; i <= N; i++ ) T[i] = ;
// process the numbers one by one
for( int i = ; i < n; i++ )
for( int j = ; j + C[i] <= N; j++ )
T[j + C[i]] += T[j];
return T[N];
}
====== 如果数组成员有重复, 但个数有限, 依旧询问组合数量 =======
比如对于每个C[i], 其数量是D[i], 这时候要引入第三层循环k, 限制循环的次数, 方向也要改为由右至左
bool T[];
bool partition( vector< int > C, vector< int > D ) {
// compute the total sum (value)
int n = C.size();
int N = ;
for( int i = ; i < n; i++ ) N += C[i] * D[i];
// initialize the table
T[] = true;
for( int i = ; i <= N; i++ ) T[i] = false;
int R = ; // rightmost true entry
// process the numbers one by one
for( int i = ; i < n; i++ ) {
for( int j = R; j >= ; j ) if( T[j] )
for( int k = ; k <= D[i] && j + k * C[i] <= N / ; k++ )
T[j + k * C[i]] = true;
R = min( N / , R + C[i] * D[i] );
}
return T[N / ];
}
====== 如果问题限制子集的集合大小, 询问组合数量 =======
这时候要做一个二维的表, k by n, k轴是数量, n轴是和, 然后用每行去生成下一行.
====== 对于标题的问题 =======
严格解
假设数组为C[], 其和为N
1. 将C[]排序
2. 构造维数组T[x], x < N+1, 初始化全部为-1, T[0]设为0
3. 从i小到大依次遍历C, 对于每个C[i]
5. 按j从N/2到0依次遍历T[], 对于每个T[j], 如果T[j]>=0, 且T[j+C[i]]<0, 则设T[j+C[i]]为i
6. 完成以上遍历, 寻找离T[N/2]最近的为true的点, 比如是T[M], 则N-2M就是最小的差绝对值.
7. 获取子集: 取T[M]的值a, 依次取T[M - C[a]]的值b, 取T[M - C[a] - C[b]]的值c....得到组合
追求性能的非严格解
假设数组为C[], 设定一个阈值为C[]数组大小, 比如M
1. 将C[]排序
2. 按顺序将C[]均分为C1[]和C2[]两个数组
3. 按相同的顺序, 依次尝试以下三种操作
a) 移动C1[i] 到 C2[]
b) 移动C2[j] 到 C1[]
c) 交换C1[i] 和 C2[j]
如果产生的新C1[]和C2[]其和的差值减小, 则重复本操作
否则i和j依次增加, 重复本次操作
4. 如果在M个上一步操作中, 无变更产生, 则调整完成
Distribute numbers to two “containers” and minimize their difference of sum的更多相关文章
- Fibonacci Numbers
Fibonacci Numbers Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) T ...
- Project Euler 44: Find the smallest pair of pentagonal numbers whose sum and difference is pentagonal.
In Problem 42 we dealt with triangular problems, in Problem 44 of Project Euler we deal with pentago ...
- Codeforces Round #356 (Div. 2)-A
A. Bear and Five Cards 题目链接:http://codeforces.com/contest/680/problem/A A little bear Limak plays a ...
- Codeforces Round #356 (Div. 2)
A. Bear and Five Cards time limit per test 2 seconds memory limit per test 256 megabytes input stand ...
- Codeforces Round #356 (Div. 2)A. Bear and Five Cards(简单模拟)
A. Bear and Five Cards time limit per test 2 seconds memory limit per test 256 megabytes input stand ...
- Educational Codeforces Round 7 D. Optimal Number Permutation 构造题
D. Optimal Number Permutation 题目连接: http://www.codeforces.com/contest/622/problem/D Description You ...
- A- Bear and Five Cards(codeforces ROUND356 DIV2)
A. Bear and Five Cards time limit per test 2 seconds memory limit per test 256 megabytes input stand ...
- Python 3.5.1 Syntax & APIs(Continue Updating..
print(x, end=' ') instead of print(x) to escape the default line-changing-output. print(str.ljust(si ...
- 【AtCoder】CODE FESTIVAL 2017 Final
A - AKIBA 模拟即可 代码 #include <bits/stdc++.h> #define fi first #define se second #define pii pair ...
随机推荐
- 我的Android第二章:Android目录结构
嗨!各位,小编又和大家分享知识啦,在昨天的博客笔记中小编给大家讲解了如何去配置Android工具以及SDK中的一些配置,那在今天的学习小编会带给大家哪些Android知识呢?首先我们看一下今天的学习目 ...
- Java中常用的查找算法——顺序查找和二分查找
Java中常用的查找算法——顺序查找和二分查找 神话丿小王子的博客 一.顺序查找: a) 原理:顺序查找就是按顺序从头到尾依次往下查找,找到数据,则提前结束查找,找不到便一直查找下去,直到数据最后一位 ...
- js简单解密(eval解密)
今天看博客园文章,看到一篇比较好的文章. 今天又学会一招,可以对一些采用eval加密的js进行解密. 打开谷歌或者火狐浏览器,然后按 F12,接着把这代码复制进去, 最后,去掉开头 4 个字母 eva ...
- linux 中的 tar 解压
Type at the command prompt tar xvzf file-1.0.tar.gz - tgfo uncompress a gzip tar file (.tgz or .tar. ...
- eclipse js提醒报错
在用eclipse开发项目时,有时候导入项目后,报错为 Problem Occurred: Errors occurred during the build. Errors running bu ...
- 【英文版本】Android开源项目分类汇总
Action Bars ActionBarSherlock Extended ActionBar FadingActionBar GlassActionBar v7 appcompat library ...
- check_user_createdate.sh
在前面这篇文章Linux如何找出用户的创建时间里面讨论了查看用户创建时间的方法,后面自己尝试弄了一个脚本来检查所有用户创建时间脚本,当然更合理的应该叫检查所有用户的密码修改时间比较准确(因为这种方法有 ...
- 简单看看这两个类 String和StringBuilder
我记得以前在园子里面讨论这两个类的文章有很多很多,并且还拿出了很多的测试报告,在什么情况下,谁比谁快,在什么情况下,该用谁 不该用谁等等这些,我这里就不比较了,我就简单看看他们里面的内部实现,那就先看 ...
- 使用dwr时动态生成table的一个小技巧
这篇随笔是我在07年写的,因为当时用了自己建设的blog,后来停止使用了,今天看到备份数据库还在,恢复出来放到这里.留着记录用. 我在使用DWR时,试了很多次都无法在动态生成的table中的一个或多个 ...
- Swift完整项目打包Framework,嵌入OC项目使用
场景说明: -之前做的App,使用Swift框架语言,混合编程,内涵少部分OC代码. -需要App整体功能打包成静态库,完整移植到另一个App使用,该App使用OC. -所以涉及到一个语言互转的处理, ...