
第一种解法:分别将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