ZOJ3987(二进制枚举+java大数)

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3987

题意:给一个数n和一个数m,让你将n这个数分成m个数相加,且这m个数的or值最小。

思路:直接从二进制角度考虑,要使得m个数的or值最小,也就是说m个数中最高位应该尽量低,我们先假设存在一个k使得

(2^k-1)*m > n > (2^(k-1)-1)*m,那么可以知道m个二进制数中至少有一个数的最高位为k。因为现在我们做或运算,所以此时应让尽量多的数的第k位为1,那么答案ans += 2^k,从高位向低位递推,直到n变为0即可。

代码:

 

import java.util.*;
import java.math.*;
public class Main{
    public static void main(String[] args){
        Scanner cin=new Scanner(System.in);
        int T=cin.nextInt();
        while(T-->0){
            BigInteger n=cin.nextBigInteger();
            BigInteger m=cin.nextBigInteger();
            BigInteger ans=BigInteger.valueOf(0);
            BigInteger nn=n;
            int len=0;
            while(nn.compareTo(BigInteger.ZERO)>0){
                nn=nn.divide(BigInteger.valueOf(2));
                len++;
            }
            for(int i=len-1;i>=0;i--){
                BigInteger num1=BigInteger.valueOf(2).pow(i).subtract(BigInteger.ONE);
                BigInteger num2=num1.multiply(m);
                while(num2.compareTo(n)<0){
                    BigInteger num3=n.divide(BigInteger.valueOf(2).pow(i));
                    if(num3.compareTo(m)>0) num3=m;
                    n=n.subtract(BigInteger.valueOf(2).pow(i).multiply(num3));
                    ans=ans.add(BigInteger.valueOf(2).pow(i));
                }
            }
            System.out.println(ans);
        }
    }
}

 

 

 

 

你可能感兴趣的:(JAVA,数论)