2017CCPC 秦皇岛 G. Numbers (贪心 + java大数)

题意:
一个数n,分成m个数a1...am (n,m很大),使a1 OR a2 OR ... OR an的值最小,输出这个最小值。
思路:

  • 首先有一个OR的性质:多个数OR,如果一个数二进制某一位是1, 最后答案这一位一定是1。那么要让结果最小,肯定每个数二进制最高的1位要小。
  • 第二:答案是个OR的最小值,那么说明答案中的最高位的1不能再减小了,(如果可以降位那就不是最小的答案了),那么这一点感性认识上可能存在方法去构造最小的答案。
  • 怎么理解呢?至少能通过方法找到最高位1的位置。那么这个题贪心的思路就有了。如果处理第i位时,m个数0 ~ i-1位全是1也不能凑出剩下的n,那第i位必然是1.由于第i位确定是1,那一定要尽可能多的让第i位是1(反正最后答案这一位都已经是1了,多一些位是1结果都一样).这样我们就能确定每一位是否是1.那就构造出答案了。
  • 这题值得多想想。
//c++写的话大致思路
int n;
int len = //n二进制位数
    
for(int i = len - 1; i >= 0 && n > 0; -- i){
    int tmp = 1 << i;
    temp = (tmp - 1) * m;
    if(temp < n){
        n = n - tmp * min(m, n / tmp);
        ans = ans | tmp;
    }
}
    cout<< ans <<endl;
import java.io.*;
import java.math.*;
import java.util.Scanner;


public class Main {
	
	static BigInteger n, m;
	
	public static void solve() {
		int len = 0;
		BigInteger zero = BigInteger.ZERO;
		BigInteger res = zero;
		
		len = n.bitLength();
		for(int i = len - 1; i >= 0 && n.compareTo(zero) > 0; i --) {
			BigInteger tmp = BigInteger.ONE.shiftLeft(i);
			BigInteger temp = (tmp.subtract(BigInteger.ONE)).multiply(m);
			if(temp.compareTo(n) < 0) {
				n = n.subtract(tmp.multiply(m.min(n.divide(tmp))));
				res = res.or(tmp);
			}
		}
		System.out.println(res);
	}
	
	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		int T; T = cin.nextInt();
		while(T -- > 0) {
			n = cin.nextBigInteger();
			m = cin.nextBigInteger();
			solve();
		}
	}
}

原文地址:https://www.cnblogs.com/A-sc/p/13786967.html