中石油训练赛 - Fermat‘s Optimization Problem(Java高精度运算+二分)

题目大意:给出误差函数 F(x,y,z,n)=|x^n+y^n-z^n|,现在给出 z 和 n,要求求出 x 和 y ,使得函数 F 的取值最小

题目分析:首先数据范围是幂次级别的,1e5 的 10 次方,也就是 1e50 次方,需要用到大数,我用的是 java 的大数类

因为 x 和 y 的取值是 x < y < z,考虑枚举 x 或 y ,然后二分去逼近另一个,这里我枚举的是 y ,将绝对值去掉后,得到 x^n+y^n-z^n,当 y 和 z 确定后,显然这个函数具有单调性,将整个式子向 0 逼近即可,即求出 x^n+y^n-z^n <= 0 的最大的可行 x1,然后尝试去更新答案,又因为绝对值是一个对称函数,不能只考虑整体小于等于 0 的最大值,还需要考虑整体大于等于 0 的最小值才行,因为 x1 是整体小于等于 0 的最大值了,所以 x1 + 1 一定是大于等于 0 的最小值,不太会证明,感性理解一下吧,所以用 x1 和 x1 + 1 各自尝试更新一下答案就好了

需要注意的点:

  1. 需要预处理出幂次 pow[ x ][ y ] = x^y,如果每次都调用 pow() 函数的话会 TLE
  2. 只能进行一次二分,进行两次二分的话带着一个常数,会 TLE
  3. 只能写二分而不能写三分,据说三分会 TLE

代码:
 

import java.math.BigInteger;
import java.util.Scanner;
  
public class Main 
{
    static Scanner cin = new Scanner(System.in);
    static public BigInteger[][] pow=new BigInteger[100005][15];
    static public int n,z,xx,yy;
    static public BigInteger ans;
    public static BigInteger cal(int x,int y)
    {
        return pow[x][n].add(pow[y][n]).subtract(pow[z][n]);
    }
    public static void solve(int y)
    {
        int l=1,r=y-1,mark=-1;
        while(l<=r)
        {
            int mid=l+r>>1;
            if(cal(mid,y).compareTo(BigInteger.ZERO)<=0)
            {
                mark=mid;
                l=mid+1;
            }
            else
                r=mid-1;
        }
        if(mark!=-1)
        {
            if(ans.compareTo(cal(mark,y).abs())>0)
            {
                ans=cal(mark,y).abs();
                xx=mark;
                yy=y;
            }
            if(mark<y-1)
            {
                if(ans.compareTo(cal(mark+1,y).abs())>0)
                {
                    ans=cal(mark+1,y).abs();
                    xx=mark+1;
                    yy=y;
                }
            }
        }
    }
    public static void main(String []args)
    {
        for(int i=1;i<=100000;i++)
        {
            pow[i][0]=BigInteger.ONE;
            for(int j=1;j<=10;j++)
                pow[i][j]=pow[i][j-1].multiply(BigInteger.valueOf(i));
        }
        BigInteger inf=BigInteger.ONE;
        for(int i=1;i<=6;i++)
            inf=inf.multiply(pow[10][10]);
        int kase=cin.nextInt();
        while(kase-->0)
        {
            n=cin.nextInt();
            z=cin.nextInt();
            ans=inf;
            for(int y=2;y<z;y++)
                solve(y);
            System.out.printf("%d %d ",xx,yy);
            System.out.println(ans);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/108913498
今日推荐