多數據集,每個數據集給出不超過35個數,求其中非空子集使得該子集數字和的絕對值最小。
如果有多個這樣的子集,取元素數目最少的。
最後輸出該子集中數字的和的絕對值,以及子集所含元素數目。
35個數,容易想到折半,對半分開搜索,這樣的話對於某一半的序列單純枚舉各數選與不選,差不多
就是2^17種,複雜度顯然可以接受。
這道題思路就是前一半先搜完(各個元素枚舉選與不選兩種狀態),然後搜到的各個非空子集可得到
sum,val,num這三個屬性,分別表示其中元素和,元素和的絕對值,以及元素數目。不妨放到容器裏存起來
然後後面一半再搜索,同樣三種屬性,不妨記爲sum',val',num'。這裏使sum'+sum絕對值最小,即要使sum最接近
-sum',這個可以用二分來實現。尋找到這樣的sum後,相應的num也可以取出(我是用HashMap容器,這樣兩
者就直接映射起來了)。然後,總的子集元素數目即num'+num。這樣處理,比較,更新答案。
上面的是主要的思路。其次,還有Java各容器的知識、應用,具體代碼如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.TreeSet;
class Reader {
static BufferedReader reader;
static StringTokenizer tokenizer;
static void init(InputStream input) {
reader = new BufferedReader(new InputStreamReader(input));
tokenizer = new StringTokenizer("");
}
static String next() throws IOException {
while (!tokenizer.hasMoreTokens()) {
tokenizer = new StringTokenizer(reader.readLine());
}
return tokenizer.nextToken();
}
static int nextInt() throws IOException {
return Integer.parseInt(next());
}
static long nextLong() throws IOException {
return Long.parseLong(next());
}
}
public class Main {
/**
* @param args
*/
static int n, len1, len2, anum, v, nnum, cnt, l, r, mid, fnum;
static long ans, sum, val, val1, val2;
static int num1, num2;
static long arr[], brr[], crr[], nownum[];
static HashMap<Long, Integer> hashMap;
static TreeSet<Long> treeSet;
private static void dfs1(int nown, int num) {
if (nown == len1 + 1) {
if (num == 0)
return;
val = Math.abs(sum);
treeSet.add(sum);
if ((val < ans) || (ans == val) && (num < anum)) {
ans = val;
anum = num;
}
if (!hashMap.containsKey(sum))
hashMap.put(sum, num);
else {
v = hashMap.get(sum);
if (v > num) {
v = num;
hashMap.put(sum, v);
}
}
return;
}
dfs1(nown + 1, num);
sum += brr[nown];
dfs1(nown + 1, num + 1);
sum -= brr[nown];
}
private static void dfs2(int nown, int num) {
if (nown == len2 + 1) {
if (num == 0)
return;
fnum = find(-sum);
val1 = Math.abs(sum + nownum[fnum - 1]);
num1 = num + hashMap.get(nownum[fnum - 1]);
if ((val1 < ans) || (ans == val1) && (num1 < anum)) {
ans = val1;
anum = num1;
}
val2 = Math.abs(sum + nownum[fnum]);
num2 = num + hashMap.get(nownum[fnum]);
if ((val2 < ans) || (ans == val2) && (num2 < anum)) {
ans = val2;
anum = num2;
}
return;
}
dfs2(nown + 1, num);
sum += crr[nown];
dfs2(nown + 1, num + 1);
sum -= crr[nown];
}
private static int find(long num) {
l = 0;
r = nnum;
while (r - l > 1) {
mid = (l + r) / 2;
if (nownum[mid] <= num)
l = mid;
else
r = mid;
}
return r;
}
private static void deal() {
if (n == 1) {
System.out.println(Math.abs(arr[1]) + " 1");
return;
}
len1 = n / 2;
brr = new long[len1 + 1];
for (int i = 1; i <= len1; i++)
brr[i] = arr[i];
len2 = n - len1;
crr = new long[len2 + 1];
for (int i = 1; i <= len2; i++)
crr[i] = arr[len1 + i];
ans = Math.abs(arr[1]);
anum = 1;
sum = 0;
hashMap = new HashMap<Long, Integer>();
treeSet = new TreeSet<Long>();
dfs1(1, 0);
treeSet.add((long) 0);
hashMap.put((long) 0, 0);
nnum = treeSet.size();
nownum = new long[nnum + 1];
Iterator<Long> iter = treeSet.iterator();
cnt = 0;
while (iter.hasNext()) {
cnt++;
nownum[cnt] = iter.next();
}
dfs2(1, 0);
System.out.println(ans + " " + anum);
}
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
Reader.init(System.in);
n = Reader.nextInt();
while (n != 0) {
arr = new long[n + 1];
for (int i = 1; i <= n; i++)
arr[i] = Reader.nextLong();
deal();
n = Reader.nextInt();
}
}
}