Project Euler 103:Special subset sums: optimum 特殊的子集和:最优解
Let S(A) represent the sum of elements in set A of size n. We shall call it a special sum set if for any two non-empty disjoint subsets, B and C, the following properties are true:
- S(B) ≠ S(C); that is, sums of subsets cannot be equal.
- If B contains more elements than C then S(B) > S(C).
If S(A) is minimised for a given n, we shall call it an optimum special sum set. The first five optimum special sum sets are given below.
n = 1: {1}
n = 2: {1, 2}
n = 3: {2, 3, 4}
n = 4: {3, 5, 6, 7}
n = 5: {6, 9, 11, 12, 13}
It seems that for a given optimum set, A = {a1, a2, … , an}, the next optimum set is of the form B = {b, a1+b, a2+b, … ,an+b}, where b is the “middle” element on the previous row.
By applying this “rule” we would expect the optimum set for n = 6 to be A = {11, 17, 20, 22, 23, 24}, with S(A) = 117. However, this is not the optimum set, as we have merely applied an algorithm to provide a near optimum set. The optimum set for n = 6 is A = {11, 18, 19, 20, 22, 25}, with S(A) = 115 and corresponding set string: 111819202225.
Given that A is an optimum special sum set for n = 7, find its set string.
NOTE: This problem is related to Problem 105 and Problem 106.
记S(A)是大小为n的集合A中所有元素的和。若任取A的任意两个非空且不相交的子集B和C都满足下列条件,我们称A是一个特殊的和集:
- S(B) ≠ S(C);也就是说,任意子集的和不相同。
- 如果B中的元素比C多,则S(B) > S(C)。
对于给定的n,我们称使得S(A)最小的集合A为最优特殊和集。前5个最优特殊和集如下所示。
n = 1: {1}
n = 2: {1, 2}
n = 3: {2, 3, 4}
n = 4: {3, 5, 6, 7}
n = 5: {6, 9, 11, 12, 13}
似乎对于一个给定的最优特殊和集A = {a1, a2, … , an},下一个最优特殊和集将是B = {b, a1+b, a2+b, … ,an+b}的形式,其中b是集合A“正中间”的元素。
应用这条“规则”,我们猜测对于n = 6的最优特殊和集将是A = {11, 17, 20, 22, 23, 24},相应的S(A) = 117。然而,事实并非如此,我们的方法仅仅只能找出近似最优特殊和集。对于n = 6,最优特殊和集是A = {11, 18, 19, 20, 22, 25},相应的S(A) = 115,对应的集合数字串是:111819202225。
若集合A是n = 7时的最优特殊和集,求其对应的集合数字串。
解题
题目坑了我好久
注意几点:
1.上面说的A的子集 B 和C ,B C 只是其中的任意两个子集,不是 B 和C的并等于A
2.A的任意子集都要满足上面两个条件,同时注意:B和C不能有交集
3.题目要求的是对于n的最优特殊和集,通过上面调整的是最近特殊和集,最优特殊和集在同样的n的情况下,其集合的和是最小的
4.7个数不相同,还是升序的
暴力解题:
1.在{19,30,37,38,39,41,44} 7个点附近寻找
2.求出所有的子集
3.对不相交的子集,利用题目给的两个条件进行求解
4.和最小的集合就是答案
下面程序参考了Mathblog,但是其是先求出每个子集的和,对于子集交集的没有看到,我改成求出所有的子集,再暴力找满足条件的值。
下面程序中在组合成初始集合A,最不好的方法也不是严格的在{19,30,37,38,39,41,44} 附近寻找的。同时,是用ArrayList当作集合用表示不是很好
JAVA
package Level4; import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.TreeSet; public class PE0103{ // 对{20, 31, 38, 39, 40, 42, 45} //255 中的每个点+-3暴力求出所有的可能
// {19,30,37,38,39,41,44} //
public static void run(){
int a[] = {19,30,37,38,39,41,44};
int min = -3;
int max = 3;
int c1[] = {19 ,30, 37, 38, 39, 41,44};
int c2[] = {19 ,31, 38, 39, 40, 42, 45};
int c3[] = {20, 31, 38, 39, 40, 42, 45};
int c4[] = {6, 9, 11, 12, 13};
int c5[] = {11, 18, 19, 20, 22, 25};
System.out.println(isOptimum(c1));
System.out.println(isOptimum(c2));
System.out.println(isOptimum(c3));
System.out.println(isOptimum(c4));
System.out.println(isOptimum(c5));
for(int a0 = a[0] ;a0<=a[0]+ max ;a0++){
for(int a1 = a0+1;a1<= a[1]+max ;a1++){
for(int a2 = a1+1;a2<=a[2]+max ;a2++){
for(int a3 = a2+1;a3<=a[3]+max ;a3++){
for(int a4 = a3+1;a4<=a[4]+max ;a4++){
for(int a5 = a4+1;a5<=a[5]+max ;a5++){
for(int a6 = a5+1;a6<=a[6]+max ;a6++){
int[] b= {a0,a1,a2,a3,a4,a5,a6};
if(isOptimum(b)){
String str = printArrStr(b); str = str + "\t SUM:"+sum(b);
System.out.println(str);
}
}
}
}
}
}
}
} }
public static String printArrStr(int[] a){
String str ="";
for(int i=0;i<a.length;i++){
str +=" "+a[i];
} return str; }
public static String printArrStr(ArrayList<Integer> list){
String str ="";
for(int i=0;i<list.size();i++){
str += " "+list.get(i);
}
return str;
}
// 验证 是否是最优子集
// 子集 一个集合可能有多个子集,而下面的程序只是看成两个自己的情况,两个子集的并集等于原来集合,照成程序有问题
public static boolean isOptimum(int[] a){
// 所有的子集
ArrayList<ArrayList<Integer>> sets = MakeSubsets(a);
int size = sets.size();
// System.out.println(size);
for(int i=0;i<size;i++){
ArrayList<Integer> set1 = sets.get(i);
for(int j=i+1;j<size;j++){
ArrayList<Integer> set2 = sets.get(j);
if(!isDisjoint(set1,set2)){
int sum1 = sum(set1);
int sum2 = sum(set2);
if(sum1 == sum2)
return false;
if(set1.size() > set2.size() && sum1 <=sum2)
return false;
if(set1.size() < set2.size() && sum1 >= sum2)
return false;
}
}
}
return true;
} // 求集合内元素的和
public static int sum(ArrayList<Integer> set){
int sum = 0;
for(int i=0;i< set.size();i++){
sum += set.get(i);
}
return sum;
}
public static int sum(int [] a){
int sum = 0;
for(int i=0;i< a.length;i++){
sum += a[i];
}
return sum;
}
// 两个子集元素是否相交 true 相交 false 不相交
public static boolean isDisjoint(ArrayList<Integer> set1,ArrayList<Integer> set2){
int size1 = set1.size();
int size2 = set2.size();
ArrayList<Integer> set = new ArrayList<Integer>();
for(int i=0;i<size1;i++){
int element = set1.get(i);
if(set.contains(element))
return true;
else
set.add(element);
}
for(int i=0;i<size2;i++){
int element = set2.get(i);
if(set.contains(element))
return true;
else
set.add(element);
}
set.clear();
return false; } // 求出所有子集的和
public static int[] MakeSubsetSums(int[] a){
int b[] = new int[(int)Math.pow(2, a.length) ];
for(int i=1;i<b.length;i++){
b[i] = MakeSubsetSum(a,i);
}
return b;
}
// 求出所有的子集
public static ArrayList<ArrayList<Integer>> MakeSubsets(int a[]){
ArrayList<ArrayList<Integer>> sets = new ArrayList<ArrayList<Integer>>();
for(int i=1;i<= (int) Math.pow(2,a.length);i++){
ArrayList<Integer> set = MakeSubset(a,i);
sets.add(set);
String s = printArrStr(set);
// System.out.println(s);
}
return sets; }
// 求出子集
public static ArrayList<Integer> MakeSubset(int[] a,int m){
ArrayList<Integer> set = new ArrayList<Integer>();
for(int i=0;i<a.length ;i++){
if( m>0 &&(m&1)==1){
set.add(a[i]);
}
m =m>>1;
}
return set;
}
// 求子集的和
// 利用 和 1 进行与运算 并移位
// 001001 相当于根据 1 所在的位置取 第 2 第 5的位置对应的数
// &000001
//----------
// 1 取出该位置对应的数
// 下面右移一位后
// 000100
// 下面同理了
public static int MakeSubsetSum(int[] a,int m){
int sum = 0;
for(int i=0;i< a.length;i++){
if( m>0 && (m&1) == 1)
sum +=a[i];
m >>=1;
}
return sum;
}
public static void main(String[] args){
long t0 = System.currentTimeMillis();
run();
long t1 = System.currentTimeMillis();
long t = t1 - t0;
System.out.println("running time="+t/1000+"s"+t%1000+"ms");
}
}
结果
20 31 38 39 40 42 45 SUM:255
20 32 39 40 41 43 46 SUM:261
20 33 40 41 42 44 47 SUM:267
21 32 39 40 41 43 46 SUM:262
21 33 40 41 42 44 47 SUM:268
22 33 40 41 42 44 47 SUM:269
running time=164s431ms
第一个就是答案,可以看出,直接根据第6个最优特征子集也可以推出下一个最优特殊子集
Python 质量也是好差
# coding=gbk import time as time
import re
import math
import numpy as np
def run():
a= [19,30,37,38,39,41,44]
f = lambda x:x+1
a0 = map(f,range(16,22))
a1 = map(f,range(27,34))
a2 = map(f,range(34,41))
a3 = map(f,range(35,42))
a4 = map(f,range(36,43))
a5 = map(f,range(37,44))
a6 = map(f,range(41,48))
for a in a0:
for b in a1:
for c in a2:
for d in a3:
for e in a4:
for f in a5:
for g in a6:
if a<b and b<c and c<d and d<e and e<f and f<g:
A = [a,b,c,d,e,f,g]
if isOptimum(A):
print A,'\t',sum(A) # [20, 31, 38, 39, 40, 42, 45] 255
# [20, 32, 39, 40, 41, 43, 46] 261
# [20, 33, 40, 41, 42, 44, 47] 267
# [20, 34, 37, 39, 40, 41, 48] 259
# [21, 32, 39, 40, 41, 43, 46] 262
# [21, 33, 40, 41, 42, 44, 47] 268
# [22, 33, 40, 41, 42, 44, 47] 269
# running time= 52.3980000019 s
def isOptimum(a):
sets = subSets(a)
# print len(sets)
for i in range(len(sets)):
for j in range(i+1,len(sets)):
s1 = sets[i]
s2 = sets[j]
if isDisjoint(s1,s2) == False:
sum1 = sum(s1)
sum2 = sum(s2)
len1 = len(s1)
len2 = len(s2)
# print s1,'\t',s2
if sum1 == sum2:return False
if len1>len2 and sum1<=sum2:return False
if len1<len2 and sum1>=sum2:return False
return True
def isDisjoint(b,c):
for bi in b:
if bi in c:
return True
return False def subSets(a):
l = 2**len(a)
sets = []
for i in range(1,l):
s = subSet(a,i)
sets.append(s)
return sets
# 求一个子集
def subSet(a,m):
s = []
for i in range(len(a)):
if (m&1)==1:
s.append(a[i])
m =m>>1
return s t0 = time.time()
run()
t1 = time.time()
print "running time=",(t1-t0),"s"
Project Euler 103:Special subset sums: optimum 特殊的子集和:最优解的更多相关文章
- Project Euler 106:Special subset sums: meta-testing 特殊的子集和:元检验
Special subset sums: meta-testing Let S(A) represent the sum of elements in set A of size n. We shal ...
- Project Euler P105:Special subset sums: testing 特殊的子集和 检验
Special subset sums: testing Let S(A) represent the sum of elements in set A of size n. We shall cal ...
- Python练习题 037:Project Euler 009:毕达哥拉斯三元组之乘积
本题来自 Project Euler 第9题:https://projecteuler.net/problem=9 # Project Euler: Problem 9: Special Pythag ...
- [project euler] program 4
上一次接触 project euler 还是2011年的事情,做了前三道题,后来被第四题卡住了,前面几题的代码也没有保留下来. 今天试着暴力破解了一下,代码如下: (我大概是第 172,719 个解出 ...
- 洛谷P1466 集合 Subset Sums
P1466 集合 Subset Sums 162通过 308提交 题目提供者该用户不存在 标签USACO 难度普及/提高- 提交 讨论 题解 最新讨论 暂时没有讨论 题目描述 对于从1到N (1 ...
- Python练习题 029:Project Euler 001:3和5的倍数
开始做 Project Euler 的练习题.网站上总共有565题,真是个大题库啊! # Project Euler, Problem 1: Multiples of 3 and 5 # If we ...
- Project Euler 9
题意:三个正整数a + b + c = 1000,a*a + b*b = c*c.求a*b*c. 解法:可以暴力枚举,但是也有数学方法. 首先,a,b,c中肯定有至少一个为偶数,否则和不可能为以上两个 ...
- Codeforces348C - Subset Sums
Portal Description 给出长度为\(n(n\leq10^5)\)的序列\(\{a_n\}\)以及\(m(m\leq10^5)\)个下标集合\(\{S_m\}(\sum|S_i|\leq ...
- 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 ...
随机推荐
- [转]vim常用命令
[转]vim常用命令 http://www.cnblogs.com/sunyubo/archive/2010/01/06/2282198.html http://blog.csdn.net/wooin ...
- opencv车道线检测
opencv车道线检测 完成的功能 图像裁剪:通过设定图像ROI区域,拷贝图像获得裁剪图像 反透视变换:用的是老师给的视频,没有对应的变换矩阵.所以建立二维坐标,通过四点映射的方法计算矩阵,进行反透视 ...
- oracle11g创建数据库最后一步确定时弹出无法创建目录
总的说是Windows7的权限问题 Windows7 dos命令下输入dbca创建数据库,因为权限问题,数据库将无法完成.所以还是在开始程序中打开dbca创建数据库比较好. Windows7 dos以 ...
- Node.js 学习(三) NPM 使用介绍
NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种: 允许用户从NPM服务器下载别人编写的第三方包到本地使用. 允许用户从NPM服务器下载并 ...
- Oracle窗口函数显示想要的行数
Oracle中支持窗口函数ROW_NUMBER(),其用法和 MSSQLServer2005中相同,比如我们 执行下面的 SQL语句: SELECT * FROM ( SELECT ROW_NUMBE ...
- 第七周技术博客发表 web网页开发
a<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org ...
- Codeforces Round #131 (Div. 1) B. Numbers dp
题目链接: http://codeforces.com/problemset/problem/213/B B. Numbers time limit per test 2 secondsmemory ...
- MYSQL导入导出.sql文件(转)
一.MYSQL的命令行模式的设置: 桌面->我的电脑->属性->环境变量->新建-> PATH=“:path\mysql\bin;”其中path为MYSQL的安装路径. ...
- PC和移动端浏览器同步测试工具Browsersync使用介绍
在移动端网页开发中,总是因为不方便调试,导致各种问题不容易被发现.但是现在有了Browsersync,一切都解决了. 不熟悉的同学可以看看Browsersync的官方网站Browsersync中文网. ...
- 鼠标滚轮事件MouseWheel
其实在大多数浏览器(IE6, IE7, IE8, Opera 10+, Safari 5+,Chrome)中,都提供了 "mousewheel" 事件.但杯具的是 Firefox ...