大数据算法系列6:面试题与ACM题选讲2

14 篇文章 1 订阅

一. POJ1700(快速过河)

http://poj.org/problem?id=1700

大意:
有n个人要过一条河,每个人过河都需要一个时间ai,有一艘船,每次过河只能最多装两个人。两个人划船过河所需的时间都取决于过河时间长的那个人。比如,A,B两人过河所需时间分别为a,b,那么,他们成一条船过河所需的时间为:max{a,b}。现在让你安排一个过河方案,让所有人用最短的时间全部过河。

分析:
n个人过河,只有一条船,一条船最多装两个人,来回时间以最多那个人为标准,问过河最短时间

首先这个问题需要分类讨论假设有m个人,按时间大小排好序

  1. m==1时,只有一个人过河 ,t+=a[1]

  2. m==2时,两个人过河,直接t取大的,t+=a[2]

  3. m==3时,三个人过河,先让最快的载最慢的过河,然后最快的回来,再在次快的过河,t+=a[3]+a[1]+a[2]

  4. m>=4时,这里要用到贪心

无论m多大,我们只看4个人,最快 次最快 最慢 次最慢

一般我们能想到的就是第一种做法,让最快的载最慢的,然后最快的回来,再载次最慢的,然后最快的回来,这里我们不需要管次最快的,这里我们就减少了两个人了,因此m-=2,在去判断,m在哪个范围再进行求解就好了。

第二种,先给个样例 1 2 1000 1000 ,这个显然用第一种做法,是行不通的,1000+1+1000=2001,我们显然可以知道,先让最快和次快过河,然后最快回来,然后最慢和次慢过河,然后次快回来,2+1+1000=1003,贪心取时间最短,所以这就是最短时间的做法,这里我们还是没有把最快和次最快过河,因此还是减少两人,m-=2。

分析结果:
第一种:最快+最慢去,最快回;最快+次慢去,最快回。
第二种:最快+次块去,最快回;最慢+次慢去,次快回。
(此时使用a,b,c,d来代替)
image.png

把上面化解一下
变为2b与a+c

代码:

package com.suanfa.数据结构;

import java.util.Arrays;
import java.util.Scanner;


public class POJ1700 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int T=sc.nextInt();  //T组数据
        for(int i=0;i<T;i++) {
            int n=sc.nextInt();
            int[] speed=new int[n];
            for(int j=0;j<n;j++) {
                speed[j]=sc.nextInt();
            }
            //排序
            Arrays.sort(speed);
            f(n,speed);  //对速度快慢进行排序
        }
    }
    private static void f(int n,int[] speed) {
        int left=n;
        int ans=0;
        while(left>0) {
            if(left==1) { //只有一个人
                ans+=speed[0];
                break;
            }else if(left==2) {  //只有2人
                ans+=speed[1];
                break;
            }
            else if(left==3) { //有3个人
                ans+=speed[2]+speed[0]+speed[1];
                break;
            }
            else {
                //1,2出发,1返回,最后两名出发,2返回
                int s1=speed[1]+speed[0]+speed[left-1]+speed[1];
                //1,3出发,1返回,1,4出发,1返回,1,2过河
                int s2=speed[left-1]+speed[left-2]+2*speed[0]; //a+2b+c
                ans+=Math.min(s1,s2);
                left-=2;  //左侧是渡河起点,left代表左侧的剩余人数
            }
        }
        System.out.println(ans);
    }

}

测试记录:
image.png

二. 面试题(链表中第k个节点)

题目:
image.png

分析:
使用双指针,让第一个指针领先第二个指针k-1,当第一个指针到达链表尾部时,第二个指针位置即n-(k-1)=n-k+1,也就是倒数第k个结点。
此题主要是让我们考虑程序鲁棒性,所以要把输入情况都要考虑在内。

代码:

public ListNode FindKthToTail(ListNode head,int k) {
        ListNode p1 = head;//快指针
        ListNode p2 = head;//慢指针
        if(head==null || k<=0){ //链表为空或k结点不再链表中
           return null;
        }
        for(int i=0;i<k-1;i++){
            if(p1.next!=null){
                p1 = p1.next;
            }
            else{
                return null;
            }
        }

        while(p1.next!=null){
                p1 =p1.next;
                p2 = p2.next;
        }
        return p2;
    }

三. 面试题(遍历二叉树)

题目:
image.png

解决方案:
参考二叉树遍历即可:https://blog.csdn.net/weixin_40025107/article/details/124903300

四. 面试题(用两个栈实现队列)

题目:
image.png

分析:
栈stack1用来实现push操作,stack2空的前提下才能进行入栈,否则影响后续进出队列的顺序。
栈stack2用来实现pop操作,将push进去的stack1内的元素存入stack2中,再进行出栈,就是队列的出列顺序。

import java.util.Stack;

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
    public void push(int node) {
        while(!stack2.isEmpty()) {
            stack1.push(stack2.pop());
        } 
        stack1.push(node);
    }
    
    public int pop() {
        while(!stack1.isEmpty()) {
            stack2.push(stack1.pop());
        }
        return stack2.pop();
    }
}

五. 面试题(判断二叉树是否为平衡二叉树)

题目:
image.png

分析:
这道题目其实和求二叉树的深度用到的方法是一样的,因为我们求二叉树的深度,其实就是求了左右子树的深度的最大值。

我们都知道如何判断一棵二叉树是不是平衡二叉树,就是它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

所以,这个时候我们只需要判断左右子树的深度之差有没有超过1,若超过了则不是平衡的,反之则为平衡二叉树。

public class Solution {
    boolean isBalanced = true;
    public boolean IsBalanced_Solution(TreeNode root) {
        TreeDepth(root);
        return isBalanced;
    }
public int TreeDepth(TreeNode root) {
        if(root == null)
            return 0;
        int l = TreeDepth(root.left);
        if(l == -1)  return -1;  // 提前返回
        int r = TreeDepth(root.right);
        if(r == -1)  return -1;  // 提前返回
        if(Math.abs(l-r) > 1){
            isBalanced = false;  // 不是平衡树
            return -1;  // 加一个标记-1,已经不可能是平衡树了
        }
        return Math.max(l,r)+1;
    }
}

六.POJ1064(二分法)

大意:
有N条绳子,长度分别为 length[1,2,3,…,N]。

如果从它们中切割出K条长度相同的绳子,这K条绳子每条最长有多长?

结果保留两位小数。

分析:
二分可能的长度。

精度问题
image.png

代码:

import java.util.Scanner;

public class Main {
    static int N, K;
    static double[] a;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        N = sc.nextInt();
        K = sc.nextInt();
        a = new double[N];
        for (int i = 0; i < N; i++)
            a[i] = sc.nextDouble();
        sc.close();
        double low = -1, high = 100001;
        while (high - low > 0.001) {
            double mid = (low + high) / 2;
            if (C(mid)) {
                low = mid;
            } else {
                high = mid;
            }
        }
        if (high < 0.01)
            System.out.println("0.00");
        else
            System.out.println(String.format("%.2f", (int) (high * 100) / 100.0));
    }

    static boolean C(double X) {
        int count = 0;
        for (int i = 0; i < N; i++)
            count = count + (int) (a[i] / X);
        return count >= K;
    }
}

七. POJ1840(简单hash)

大意:
给出ai(i=1~5),求
a 1 ∗ x 1 3 + a 2 ∗ x 2 3 + a 3 ∗ x 3 3 + a 4 ∗ x 4 3 + a 5 ∗ x 5 3 = 0 a_1 * x_1^3+ a_2 * x_2^3+ a_3 * x_3^3+ a_4 * x_4^3+ a_5 * x_5^3=0 a1x13+a2x23+a3x33+a4x43+a5x53=0
在-50到50之间的x的解的个数

分析:
将等式转化为
− ( a 1 x 1 3 + a 2 x 2 3 ) = a 3 x 3 3 + a 4 x 4 3 + a 5 x 5 3 -(a_1x_1^3+ a_2x_2^3) = a_3x_3^3+ a_4x_4^3+ a_5x_5^3 (a1x13+a2x23)=a3x33+a4x43+a5x53
这样就直接减少了时间复杂度

将等式左边值设为Hash的下标,为了不为负,左右两边都加上一个$5050^33 = 18750000 $
所以要加一个比它大的数,这里用的是25000000

八. POJ3465(贪心+反悔)

大意:
剑圣在单挑大龙
剑圣每一次放技能龙就会打他一下,剑圣就三个技能:

  1. 对龙攻击造成减少X点血
  2. 躲掉龙的攻击
  3. 回血Y点

龙N次攻击造成减少血量为 ai
初始状态下剑圣和龙的血量分别是 H1 H2

问剑圣是否可以solo过大龙,并输出相应的数据

分析:
每一次都攻击大龙,直到剑圣要死了,时光倒流,选择前面某一次进行回血或者躲避大龙的攻击

也就是说贪心攻击,时光倒流的时候用优先队列选择即可

九. POJ2002(哈希)

大意:
给定n个相异整数点,求能构成多少个正方形。

分析:
其实这题挺简单的,经过思考可以发现,我们可以通过枚举两个点(或者说一条边),然后求出可能构成正方形的另外两个点,然后判断是不是在给你的点集中就行了。求点问题是一个数学问题,判断要用到hash表。

十. POJ2786(贪心 + 优先队列)

题目:
image.png

分析:
用一个优先队列进行模拟(priority_queue)order的情况。
先按照截止日期due对Order进行从小到大的排序,用pass表示当前完成最多订单需要的最短的时间,
遍历Order,当pass + order[i].q <= order[i].d的时候,
表示可以正常完成该订单,进队,同时pass += order[i].q,
如果pass + order[i].q > order[i].d的时候,
则需要考虑order[i].q和队列中需要最长时间的订单之间的关系,
如果order[i].q较大,说明该订单不可能完成,否则入队,pass += order[i].q,
然后要减去队列中需要最长时间的订单(即队首),一直贪心,最后留在队列中的订单个数就是保留的订单个数。

结论:
其实题目的意思是保留最多的一个订单数,而非订单量,所以用贪心算法,一直到>=截止日期的时候,如果发现当前任务添加上去, 会超出最后期限, 选择当前任务和之前任务中最大的生产时间进行放弃.

参考:

  1. http://www.dataguru.cn/article-5747-1.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值