Online Judge | Problem Set | Authors | Online Contests | User | ||||||
---|---|---|---|---|---|---|---|---|---|---|
Web Board Home Page F.A.Qs Statistical Charts | Current Contest Past Contests Scheduled Contests Award Contest |
贴Java代码。 半枚举+二分,带注释// 如果是n个值进行枚举的话计算量是O(2^n),绝壁hold不住 // 所以把这些值分成两半,分别枚举,然后在组合这两个数组的时候用二分法避免了全列举。 // 当时做这道题目的时候写到一半发现不太对头,所以大改过(只比重写稍微轻松了一点点)所以代码看起来比较蛋疼且冗余。。。 //A了就懒得改了。嫌麻烦的只看注释吧2333 import java.io.*; import java.util.*; public class Main{ static int N; // 记录输入的数字 static long nums[]; // 这两个动态数组分别记录分成两半后各自枚举的所有情况 static ArrayList<subset> ssL = new ArrayList<subset>(); static ArrayList<subset> ssR = new ArrayList<subset>(); // 这两个数组用于枚举(取不取当前数字?取的话设为1,不取设为0) static int[] iruL; static int[] iruR; // 最小的绝对值 static long ans; // 最小的元素个数 static int ansc; // 下面2个函数把N个值分成两半然后分别枚举,各自最多为2^17,2^18种情况 static void generate_subsetL(int index){ if(index == N/2){ long sum = 0; int count = 0; for(int i = 0; i < iruL.length; i++){ if(iruL[i] == 1){ count++; sum += nums[i]; } } ssL.add(new subset(sum,count)); return; } // 用递归进行枚举 for(int i = 0; i < 2; i++){ iruL[index] = i; generate_subsetL(index+1); } } static void generate_subsetR(int index){ if(index == N-N/2){ long sum = 0; int count = 0; for(int i = 0; i < iruR.length; i++){ if(iruR[i] == 1){ count++; sum += nums[i+N/2]; } } ssR.add(new subset(sum,count)); return; } for(int i = 0; i < 2; i++){ iruR[index] = i; generate_subsetR(index+1); } } // 组合两个数组。因为ssL数组规模小于等于ssR,所以对ssL进行了排序 // 然后循环ssR中每个元素,二分ssL private static void solve() { // 循环ssR的所有元素 for(int q = 0; q < ssR.size(); q++){ long sum = ssR.get(q).sum; int count = ssR.get(q).count; // 用二分法逼近答案,因为可能会有多解的情况 // 所以需要进行两次,第一次寻找最接近0的负值,第二次寻找最接近0的正值。 // 然后扫描这两者之间的所有元素,找出sum最小,且count最小的组合 // 先找最接近0的负值,二分后取左边界 int l = 0; int r = ssL.size(); while(r-l>1){ int m = (l+r)/2; if(ssL.get(m).sum + sum >=0){ r = m; }else{ l = m; } } int L = l; // 然后找最接近0的正值,二分后取右边界 l = -1; r = ssL.size()-1; while(r-l>1){ int m = (l+r)/2; if(ssL.get(m).sum + sum >0){ r = m; }else{ l = m; } } int R = r; // 扫描L到R中所有情况,与ans对比 // L到R最少1种情况,最多13w种,不过只要出现ans=0,ansc=1就可以直接输出了。不可能出现要扫那么多情况的情况。 for(int i = L; i <= R; i++){ long tempsum = Math.abs(ssL.get(i).sum + sum); int tempcount = ssL.get(i).count + count; // 排除掉空集的情况 if (tempcount == 0) continue; if(tempsum < ans){ ans = tempsum; ansc = tempcount; } else if(tempsum == ans & tempcount < ansc){ ansc = tempcount; } if(ans == 0 & ansc == 1) return; } } } public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); while(true){ N = new Integer(br.readLine()); if(N==0) break; nums = new long[N]; iruL = new int[N/2]; iruR = new int[N-N/2]; ans = 1000000000000001l; ansc = 36; String input[] = br.readLine().split("\\s"); for(int i = 0; i < N; i++){ nums[i] = new Long(input[i]); } // 分成两半后分别枚举 ssL.clear(); ssR.clear(); generate_subsetL(0); generate_subsetR(0); // 因为ssL数组规模小于等于ssR,所以对ssL进行了排序 Collections.sort(ssL); solve(); System.out.println(ans+" "+ansc); } } } //子集。变量是sum:该子集的元素和,count:该子集的元素个数 class subset implements Comparable<subset>{ long sum; int count; subset(long sum, int count){ this.sum = sum; this.count = count; } public int compareTo(subset o) { if(this.sum - o.sum < 0) return -1; else return 1; } } Followed by: Post your reply here: |
All Rights Reserved 2003-2013 Ying Fuchen,Xu Pengcheng,Xie Di
Any problem, Please Contact Administrator