Java笔试题04-11

1.小虎同学打算在华东区域的多个地区开设某虎工场店,他希望以最少的数量覆盖所有目标地区,并降低成本。为了达到这个目标,他将这些地区抽象为平面上的坐标点,并根据一条简单的规则来判断地区间是否能够连接。

规则很简单:如果两个地区的坐标点在同一行或同一列,就认为它们之间存在道路,可以用工场店覆盖。反之,如果地区坐标不在同一行或同一列,就认为它们之间不存在道路,需要额外的工场店才能覆盖。

现在的问题是:对于一组给定的地区,如果我们希望某虎公司的服务可以覆盖到所有目标地区,那么小虎同学最少需要开设几家工场店呢?

通过合理的规划和计算,我们可以得出最优的答案,帮助小虎同学在最少的工场店数量下,实现最全面的服务覆盖。

输入例子1:[[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]]

输出例子:1

例子说明:任意一个地区开设工场店,都可以满足服务覆盖。所以,最少需要开设1家工场店。

输入例子2:[[0,0],[0,2],[1,1],[2,0],[2,2]]

输出例子:2

例子说明:[1,1]地区需要单独建设工场店;其它地区,可以任意选择一个地区建设工场店即可完成服务覆盖。所以,最少需要开设2家工场店。

///并查集
public class Solution {
     
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 最少需要开设的工场店数量
     * @param cities int整型二维数组 城市坐标
     * @return int整型
     */
    public  int minFactoryStores(int[][] cities) {
         int[] farther=new int[cities.length];
        for (int i = 0; i < cities.length; i++) {
                farther[i] = i;
            }

        // 遍历所有地区对,合并同一行或同一列的地区  
        for (int i = 0; i < cities.length; i++) {
            for (int j = i + 1; j < cities.length; j++) {
                if (cities[i][0] == cities[j][0] || cities[i][1] == cities[j][1]) {
                    union(farther,i, j);
                }
            }
        }

        // 统计连通区域数量  
        int count = 0;
        for (int i = 0; i < cities.length; i++) {
            if (find(farther,i) == i) {
                count++;
            }
        }

        return count;
    }

       public int find(int[] farther,int x) {
            if (farther[x] != x) {
                farther[x] = find(farther,farther[x]);
            }
            return farther[x];
        }
          public void union(int[] farther,int x, int y) {
            int rootX = find(farther,x);
            int rootY = find(farther,y);
            if (rootX != rootY) {
                farther[rootX] = rootY;
            }
        }

 2.二维数组求左上角到最下角的最短路径和/成本。

 /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给出一个仓库库位二维分布平面图,计算从仓库的最左上角开始拣货,到最右下角拣货完成的成本最少并返回。
     * @param grid int整型二维数组 表示仓库库位二维分布平面图。其中,grid[i][j] 表示在地点 (i, j) 的成本
     * @return int整型
     */
    public int minPathSum (int[][] grid) {
        if(grid.length==1 && grid[0].length==1){
            return grid[0][0];
        }
        // write code here
        int[][] dp= new int[grid.length][grid[0].length];
        dp[0][0]=grid[0][0];
        for(int i=1;i<grid.length;i++){
            dp[i][0]=dp[i-1][0]+grid[i][0];
        }
         for(int j=1;j<grid[0].length;j++){
            dp[0][j]=dp[0][j-1]+grid[0][j];
        }
          for(int i=1;i<grid.length;i++){
            for(int j=1;j<grid[0].length;j++){
                    dp[i][j]=grid[i][j]+Math.min(dp[i-1][j],dp[i][j-1]);
            }
        }
        return dp[grid.length-1][grid[0].length-1];
    }

3.途小虎平台提供的以下几个服务及其价格和持续时间(模拟),用户可以根据自己的预算和可用时间选择合适的服务:

服务价格持续时间
轮胎安装201
汽车保养502
汽车清洗301
汽车美容603
汽车维修402

现在,给定用户的预算和可用时间,请推荐用户可以选择的所有服务组合,使得总价格不超过预算,总时间不超过可用时间,并且总价格尽可能大。(三维动态规划不会!!!)

/**这个Java代码首先定义了一个Service类来表示服务及其价格和持续时间。findCombinations方法接受服务列表、预算和可用时间,并返回所有不超过预算和时间的服务组合列表。backtrack方法是一个递归方法,用于生成所有可能的服务组合。在main方法中,我们初始化服务列表、用户预算和可用时间,并调用findCombinations方法来找到所有可能的服务组合,并将它们打印出来。

请注意,这个实现没有特别优化以找到总价格最大的组合。它简单地生成了所有可能的组合,并检查它们是否满足预算和时间的限制。在实际应用中,您可能需要添加额外的逻辑来优化搜索过程,以便更高效地找到总价格最大的组合。*/
public class ServiceCombinationFinder {  

    static class Service {  

        String name;  

        int price;  

        int duration;  

  

        Service(String name, int price, int duration) {  

            this.name = name;  

            this.price = price;  

            this.duration = duration;  

        }  

    }  

    public static List<List<Service>> findCombinations(List<Service> services, int budget, int time) {  

        List<List<Service>> combinations = new ArrayList<>();  

        backtrack(services, 0, budget, time, new ArrayList<>(), combinations);  

        return combinations;  

    }  

  

    private static void backtrack(List<Service> services, int start, int remainingBudget, int remainingTime,  

                                    List<Service> currentCombination, List<List<Service>> combinations) {  

        // 如果当前组合的总价格不超过预算且总时间不超过可用时间,则保存这个组合  

        if (remainingBudget >= 0 && remainingTime >= 0) {  

            combinations.add(new ArrayList<>(currentCombination));  

        }  

  

        // 尝试添加每个服务到当前组合中  

        for (int i = start; i < services.size(); i++) {  

            Service service = services.get(i);  

            int cost = service.price;  

            int timeCost = service.duration;  

  

            // 如果添加当前服务会导致预算或时间超出限制,则跳过  

            if (cost > remainingBudget || timeCost > remainingTime) {  

                continue;  

            }  

  

            // 添加当前服务到组合中  

            currentCombination.add(service);  

            // 递归调用,尝试添加下一个服务  

            backtrack(services, i + 1, remainingBudget - cost, remainingTime - timeCost, currentCombination, combinations);  

            // 回溯,移除当前服务  

            currentCombination.remove(currentCombination.size() - 1);  

        }  

    }  

  

    public static void main(String[] args) {  

        // 初始化服务列表  

        List<Service> services = new ArrayList<>();  

        services.add(new Service("轮胎安装", 20, 1));  

        services.add(new Service("汽车保养", 50, 2));  

        services.add(new Service("汽车清洗", 30, 1));  

        services.add(new Service("汽车美容", 60, 3));  

        services.add(new Service("汽车维修", 40, 2));  

  

        // 用户预算和可用时间  

        int budget = 100;  

        int time = 4;  

  

        // 查找所有可能的服务组合  

        List<List<Service>> combinations = findCombinations(services, budget, time);  

  

        // 打印所有可能的服务组合  

        for (List<Service> combination : combinations) {  

            System.out.println("组合:");  

            for (Service service : combination) {  

                System.out.println("  " + service.name + " - 价格: " + service.price + " - 持续时间: " + service.duration);  

            }  

            System.out.println();  

        }  

    }  

}

3.小红拿到了一个正整数,她准备随机选择该数的一个数位,并将这个数位随机改成'1'~'9'中任意一个数字(请注意,修改后可能和原数相同)。小红想知道,她修改一次后,这个数变成素数的概率是多少?

输入描述:

一个正整数  , 1<=n<=10的9次方

注意:整数的最大范围为2147483648=2 * 10的9次方,所以这一题会溢出,但由于质数都是大于0,所以溢出就不用计入总数。

输出描述:

一个浮点数,代表最终得到素数的概率。如果你的答案和标准答案的相对误差不超过10^-5,则认为你的答案正确。
 private static int calculateDigit(int n){
        int count=0;
        while(n!=0){
            count++;
            n/=10;
        }
        return count;
    }
    private static boolean isPrime(int n){
        if(n<=1){
            return false;
        }
        for(int i=2;i<n/i;i++){
            if(n%i==0){
                return false;
            }
        }
        return true;
    }
    public static void main(String[] args) {
        //素数等于质数,都大于1
        Scanner in = new Scanner(System.in);
        int num = in.nextInt();
        int countDigit=calculateDigit(num); //计算位数
        int primeCount=0;
        for(int i=0;i<countDigit;i++){
            int factor=(int)Math.pow(10,countDigit-1-i);
            int digit=(num/factor)%10;
            for(int j=1;j<=9;j++){
                int newNum=num-factor*digit+j*factor;
                if(isPrime(newNum)){  
                    primeCount++;
                }
            }
        }
        System.out.println(primeCount); //素数个数/总个数,总个数=位数*9
        System.out.print((double) primeCount/(countDigit*9));//一定要转double不然会直接成0
    }

4.假如有一排房子,共 n 个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。

当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3 的正整数矩阵 costs 来表示的。

例如,costs[0][0] 表示第 0 号房子粉刷成红色的成本花费;costs[1][2] 表示第 1 号房子粉刷成绿色的花费,以此类推。

请计算出粉刷完所有房子最少的花费成本。

注意:两个循环的顺序可以互换,来解决问题。

 //空间为O(n)
 public int minCost(int[][] costs) {
        int[][] dp = new int[costs.length][3];
        for (int j = 0; j < 3; j++) {
            dp[0][j] = costs[0][j];
        }

        for (int j = 1; j < costs.length; j++) {
            for (int i = 0; i < 3; i++) {
                dp[j][i] = Math.min(dp[j-1][(i + 1) % 3], dp[j-1][(i + 2) % 3]) + costs[j][i];
            }
        }

        return Math.min(dp[costs.length - 1][0], Math.min(dp[costs.length - 1][1], dp[costs.length - 1][2]));

    }
//空间为O(1)
public int minCost(int[][] costs) {
        int[][] dp = new int[costs.length][3];
        for (int j = 0; j < 3; j++) {
            dp[0][j] = costs[0][j];
        }
        //设定三个状态转移方程,r(i)=min(g(i-1),b(i-1))
        //g(i)=min(r(i-1),b(i-1)),b(i)=min(g(i-1),r(i-1))
        //画出n*3的表格
        for (int j = 1; j < costs.length; j++) {
            for (int i = 0; i < 3; i++) {
                dp[j][i] = Math.min(dp[j-1][(i + 1) % 3], dp[j-1][(i + 2) % 3]) + costs[j][i];
            }
        }
        return Math.min(dp[costs.length - 1][0], Math.min(dp[costs.length - 1][1], dp[costs.length - 1][2]));
    }

5.打家劫舍

一个专业的小偷,计划偷窃一个环形街道上沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组 nums ,请计算 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

Arrays.copyOfRange()函数的的使用,不包括最后一位下标的复制

   public int rob(int[] nums) {
        //环形房屋注意,第一家偷盗了,最后一家就不能偷盗了
        //将问题分解为两个子问题进行动态规划,房屋0到n-1与房屋1到n,求二者的最大值
       if(nums.length==0){
        return 0;
       }
       if(nums.length==1){
        return nums[0];
       }
       int[] a1=Arrays.copyOfRange(nums,0,nums.length-1);
       int[] a2=Arrays.copyOfRange(nums,1,nums.length);
       return Math.max(Rob1(a1),Rob1(a2));
    }
    private int Rob1(int[] nums){
        if(nums.length==1){
            return nums[0];
              }
        int[] dp=new int[2];
        dp[0]=nums[0];
        dp[1]=Math.max(nums[0],nums[1]);
        for(int i=2;i<nums.length;i++){
            dp[i%2]=Math.max(dp[(i-1)%2],dp[(i-2)%2]+nums[i]);
        }
        return dp[(nums.length-1)%2];
    }

6.给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。

字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)

题目数据保证答案符合 32 位带符号整数范围。

1.解法一
//确认动态转移方程,是双序列问题,一般都是和字符串有关,一般数组长度到len+1
   //为什么从-1开始呢,因为从开始状态转移方程不具备普遍性。
   //形式为dp[i+1][j+1]=dp[i][j]的样子
        /*
        当f(i,j)表示字符串s1下标从0到i的字串能否和s2下标从0到j的字串
        组成形成s3下标从0到i+j+1的字串.
        当下标i不等于下标j,f(i,j)=f(i-1,j-1)
        当下标i等于下标j,f(i,j)=f(i-1,j-1)+f(i-1,j)
        f(-1,j)=0,f(i,-1)=1,f(-1,-1)=1
        */
        int len1=text1.length();
        int len2=text2.length();
        if(len1<len2){
            return 0;
        }
        int[][] dp=new int[len1+1][len1+1];
        for(int i=0;i<len1;i++){
            dp[i][0]=1;
        }
         for(int j=0;j<len2;j++){
            dp[0][j]=0;
        }
        dp[0][0]=1;
        for(int i=0;i<len1;i++){
            for(int j=0;j<len2 ;j++){
                if(text1.charAt(i)==text2.charAt(j)){
                  dp[i+1][j+1]=dp[i][j]+dp[i][j+1]; 
                }else{
                  dp[i+1][j+1]=dp[i][j+1];
                }
            }
        }
        return dp[len1][len2];
    }

2.解法二,一位数组+两个变量保存左上方和正上方值
public int numDistinct(String text1, String text2) {
        //在计算第i列时,需要计算左上方和正上方的值,我们选用两个变量来保存
        //prev和cur,一定要先保存prev的值再将cur的值赋值给dp[j+1]
        int len1=text1.length();
        int len2=text2.length();
        if(len1<len2){
            return 0;
        }
        int[] dp=new int[len2+1];
        dp[0]=1;
        for(int i=0;i<len1;i++){
            int prev=dp[0];
            for(int j=0;j<len2 && i>=j ;j++){
                int cur;
                if(text1.charAt(i)==text2.charAt(j)){
                  cur=prev+dp[j+1]; 
                }else{
                  cur=dp[j+1];
                }
                prev=dp[j+1];
                dp[j+1]=cur;
            }
        }
        return dp[len2];
    }

3.解法三,倒叙保存,先更新第最后一行,由于第i列同时存储着第f(i-1,j)和f(i-1,j-1)的值,我们又需要这两个值,所以从右往左不会覆盖f(i-1,j-1)的值

  public int numDistinct(String text1, String text2) {
        int len1=text1.length();
        int len2=text2.length();
        if(len1<len2){
            return 0;
        }
        int[] dp=new int[len2+1];
        dp[0]=1;
        for(int i=0;i<len1;i++){
            for(int j=len2-1;j>=0 ;j--){
                if(text1.charAt(i)==text2.charAt(j)){
                 dp[j+1]=dp[j+1]+dp[j]; 
                }
            }
        }
        return dp[len2];
    }

7.给定三个字符串 s1s2s3,请判断 s3 能不能由 s1 和 s2 交织(交错) 组成。

两个字符串 st 交织 的定义与过程如下,其中每个字符串都会被分割成若干 非空 子字符串:

  • s = s1 + s2 + ... + sn
  • t = t1 + t2 + ... + tm
  • |n - m| <= 1
  • 交织s1 + t1 + s2 + t2 + s3 + t3 + ... 或者 t1 + s1 + t2 + s2 + t3 + s3 + ...

提示:a + b 意味着字符串 ab 连接。

public boolean isInterleave(String s1, String s2, String s3) {
        if (s1.length() + s2.length() != s3.length()) {
            return false;
        }
        boolean[][] dp = new boolean[s1.length() + 1][s2.length() + 1];
        dp[0][0] = true;
        for (int i = 0; i < s1.length(); i++) {
            if (s1.charAt(i) == s3.charAt(i) && dp[i][0])
                dp[i + 1][0] = true;
        }
        for (int j = 0; j < s2.length(); j++) {
            if (s2.charAt(j) == s3.charAt(j) && dp[0][j])
                dp[0][j + 1] = true;
        }
        for (int i = 0; i < s1.length(); i++) {
            for (int j = 0; j < s2.length(); j++) {
                char ch1 = s1.charAt(i);
                char ch2 = s2.charAt(j);
                char ch3 = s3.charAt(i + j + 1);
                if (ch1 == ch3 && dp[i][j + 1]) {
                    dp[i + 1][j + 1] = true;
                } else if (ch2 == ch3 && dp[i + 1][j]) {
                    dp[i + 1][j + 1] = true;
                }
            }
        }
        return dp[s1.length()][s2.length()];
    }

8.给定整数 n ,返回 所有小于非负整数 n 的质数的数量

 //埃式筛选法,遍历2到根号n,将该区间内所有质数2的倍数的数标记为合数,剩余的为质数
    public int countPrimes(int n) {
        if (n <= 1) {
            return 0;
        }
        int count = 0;
        boolean[] nums=new boolean[n+1];
        Arrays.fill(nums,true);
        for (int i = 2; i <= n/i; i++) {
            if (isPrime(i)) {
                for(int j=i*i;j<n;j+=i){
                    nums[j]=false;
                }
            }
        }
        for(int i=2;i<n;i++){
            if(nums[i]){
                count++;
            }
        }
        return count;
    }

    private boolean isPrime(int n) {
        if (n < 2) {
            return false;
        }  for(int i=2;i*i<=n;i++){会溢出
        for (int i = 2; i <= n / i; i++) {
            if (n % i == 0) {
                return false;
            }
        }
        return true;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值