第一种解法:分别将A和B对于n个shift的所有组合情况计算出happy值,对于happy value>=H的组合,分别保存在seta和setb中。枚举seta中的每个组合 i,找到setb中能与 i 进行或运算得到(1<<n) -1的组合 j,这表明A的一个组合i :happy value>=H, B的一个组合j: happy value >=H,且A的这个组合和B的这个组合满足n shift都至少有1人值班。(这里运用了一个原理:如果 I |J =( 1<<n) - 1,那么J的super set K,也满足I | K = (1<<n)-1), 用SOS DP计算某个组合有多少个super set.
注:进行DP的二维数组num, 可以优化为一维数组,为了便于理解递推关系,这里用的二维。
关于SOS DP,这里有一篇比较详细的文章:http://codeforces.com/blog/entry/45223
与文章中不同的是,这里是求一个组合的super set(父集), 而文章中是介绍求一个组合(集合)的subset(子集)。不论是父集还是子集,其内在的思想是一样的:例如101010111,第4位是0,那么这个组合的父集的第4位可以是0或1;第3位是1,那么这个组合的父集第3位只能是1。(子集恰好相反,这个组合的子集第4位只能是0,而子集第3位可以是0或1)。
num[i][mask] 是表示与组合mask“前i位不尽相同,高位相同”的子集的数目.
1 import java.util.Scanner; 2 import java.util.HashSet; 3 public class GCJ_KI2019G_C5{ //SOS DP 4 public static void main(String[] args){ 5 Scanner in = new Scanner(System.in); 6 int T = in.nextInt(); 7 for(int cas = 1; cas <= T; cas++){ 8 int n = in.nextInt(); 9 long h = in.nextLong(); 10 long[] a = new long[n]; 11 long[] b = new long[n]; 12 long sumA = 0L; long sumB = 0L; 13 for(int i = 0; i < n; i++){ 14 a[i] = in.nextLong(); 15 sumA += a[i]; 16 } 17 for(int i = 0; i < n; i++){ 18 b[i] = in.nextLong(); 19 sumB += b[i]; 20 } 21 if(sumA < h || sumB < h){ 22 System.out.println("Case #"+cas+": "+0); 23 continue; 24 } 25 26 HashSet<Integer> seta = new HashSet<>(); 27 recur(a,0,h,0L,0,seta); 28 HashSet<Integer> setb = new HashSet<>(); 29 recur(b,0,h,0L,0,setb); 30 31 int[][] num = new int[n+1][1<<n]; 32 int x = (1<<n) - 1; 33 for(int v: setb) 34 num[0][v] = 1; 35 36 for(int i = 1; i <= n; i++){ //SOS DP 37 for(int mask = 0; mask <= x; mask++){ 38 num[i][mask] = num[i-1][mask]; 39 if((mask & (1<<(i-1))) == 0) 40 num[i][mask] += num[i-1][mask|1<<(i-1)]; 41 } 42 } 43 44 long ans = 0l; 45 for(int v: seta){ 46 ans += num[n][x-v]; 47 } 48 System.out.println("Case #"+cas+": "+ans); 49 } 50 } 51 52 static void recur(long[] arr, int p, long h, long accu, int bitmask, HashSet<Integer> set){ 53 if(p == arr.length){ 54 if(accu >= h){ 55 set.add(bitmask); 56 } 57 return; 58 } 59 recur(arr,p+1,h,accu, bitmask,set); 60 recur(arr,p+1,h,accu+arr[p], bitmask|(1<<p),set); 61 } 62 }
*******************************************分割线***********************************************
第二种解法:
将n个shift分成两段,0~n/2与n/2+1~n-1
每一段都会形成若干对happy value,对于第一段中的happy value pair,若第二段中存在happy value pair,能够满足pair1.first + pair2.first >= h且 pair1.second + pair2.second >= h, 那么这是一个有效的解。
对于第一段中的一个pair, 运用线段树可以较快的找出第二段中满足以上条件的pair的数目。
import java.util.Scanner; import java.util.ArrayList; import java.util.Collections; public class Solution{ public static void main(String[] args){ Scanner in = new Scanner(System.in); int T = in.nextInt(); for(int cas = 1; cas <= T; cas++){ int n = in.nextInt(); int h = in.nextInt(); long[] a = new long[n]; long[] b = new long[n]; long sumA = 0L; long sumB = 0L; for(int i = 0; i < n; i++){ a[i] = in.nextLong(); sumA += a[i]; } for(int i = 0; i < n; i++){ b[i] = in.nextLong(); sumB += b[i]; } if(sumA < h || sumB < h){ System.out.println("Case #"+cas+": "+0); continue; } ArrayList<Pair> list1 = new ArrayList<>(); recur(a,b,0,n/2,0L,0L,list1,h); ArrayList<Pair> list2 = new ArrayList<>(); recur(a,b,n/2+1,n-1,0L,0L,list2,h); Collections.sort(list1); Collections.sort(list2); SegTree tree = new SegTree(0,h); long ans = 0; int p = list2.size() - 1; for(Pair pair: list1){ while(p >= 0 && list2.get(p).fir >= h - pair.fir){ tree.add(list2.get(p).sec); p--; } ans += tree.get(h - pair.sec, h); } System.out.println("Case #"+cas+": "+ans); } } static class SegTree{ int low, high; SegTree left, right; int sum = 0; public SegTree(int a, int b){ low = a; high = b; } public void add(int v){ sum++; if(low == high) return; int m = low + (high-low)/2; if(v <= m){ if(left == null) left = new SegTree(low,m); left.add(v); } else{ if(right == null) right = new SegTree(m+1,high); right.add(v); } } public int get(int a, int b){ if(a <= low && b >= high) return sum; if(a > high || b < low) return 0; int res = 0; if(left != null) res = left.get(a,b); if(right != null) res += right.get(a,b); return res; } } static class Pair implements Comparable<Pair>{ int fir, sec; public Pair(int a, int b){ fir = a; sec = b; } public int compareTo(Pair pair){ return Integer.compare(fir, pair.fir); } } static void recur(long[] a, long[] b, int p, int end, long accuA, long accuB, ArrayList<Pair> list,int h){ if(p > end){ list.add(new Pair((int)Math.min(h,accuA), (int)Math.min(h,accuB))); return; } recur(a,b,p+1,end,accuA+a[p],accuB,list,h); recur(a,b,p+1,end,accuA, accuB+b[p],list,h); recur(a,b,p+1,end,accuA+a[p],accuB+b[p],list,h); } }
Kick Start 2019 Round G Problem C shifts 两种解法(SOS DP, Segment tree)
原文:https://www.cnblogs.com/black-fish/p/11756428.html