8天刷108道华为机考题

本文介绍了四个算法问题的解决方案,涉及动态规划在购物单优化中的应用,实现错误记录功能的逻辑,计算合唱队形最少出列人数的方法,以及通过前序遍历和中序遍历重建二叉树的算法。这些题目涵盖了数据结构和算法的不同方面,有助于提升编程思维和问题解决能力。
摘要由CSDN通过智能技术生成

之前已经用c++做了一遍,这次再用java来一遍,记录下来,监督自己。
简单题就不记录了,值得回味的记录一下。

1.购物单(动态规划,基于knapsack01问题的拓展)
题目描述
王强今天很开心,公司发给N元的年终奖。王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件 附件
电脑 打印机,扫描仪
书柜 图书
书桌 台灯,文具
工作椅 无
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。王强想买的东西很多,为了不超出预算,他把每件物品规定了一个重要度,分为 5 等:用整数 1 ~ 5 表示,第 5 等最重要。他还从因特网上查到了每件物品的价格(都是 10 元的整数倍)。他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第 j 件物品的价格为 v[j] ,重要度为 w[j] ,共选中了 k 件物品,编号依次为 j 1 , j 2 ,……, j k ,则所求的总和为:
v[j 1 ]*w[j 1 ]+v[j 2 ]*w[j 2 ]+ … +v[j k ]*w[j k ] 。(其中 * 为乘号)
请你帮助王强设计一个满足要求的购物单。

输入描述:
输入的第 1 行,为两个正整数,用一个空格隔开:N m

(其中 N ( <32000 )表示总钱数, m ( <60 )为希望购买物品的个数。)

从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q

(其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 ~ 5 ), q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号)

输出描述:
输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值( <200000 )。
示例1
输入
复制
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
输出
复制
2200

解题思路:
1.先看knapsack01,N件物品价值不同,重量不同,现将他们尽可能的装入一个承载量为M公斤的背包里,问能带走的最大价值是多少?
状态转移方程:dp[n][m] = max(dp[n-1][m], dp[n-1][m-w[n]]+v[n])
dp[n-1][m]表示不包含第n件物品,承载量达到m时能获取的最大价值;
dp[n-1][m-w[n]]+v[n]表示包含第n件物品能获得的最大值;
然后两个值再取最大值就是n件商品装入重量上线为m的背包中能带走的最大价值。

2.再看购物车这道题,主件存在,附件才能被购买,所以附件跟主件绑定,单独的附件是不可以直接购买的。一个主件最多有两个附件,所以一个主件能贡献的最大值至多只需要考虑四种情况:买主件、买主件+附件1、买主件+附件2、买主件+附件1+附件2。
状态转移方程:dp[n][m] = max(dp[n-1][m], dp[n-1][m-m[k]]+v[k])

import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int money = sc.nextInt();
        int n = sc.nextInt();
        MainGood[] goods = new MainGood[n+1];
        goods[0] = new MainGood(0,0,0);
        for(int i=0;i<n;i++){
            int tempP = sc.nextInt();
            int tempPri = sc.nextInt();
            int tempC = sc.nextInt();
            if(tempC > 0){
                if(goods[tempC].subCode1 == 0){
                    goods[tempC].setSubCode1(i+1);
                }else{
                    goods[tempC].setSubCode2(i+1);
                }
                goods[i+1] = new MainGood(tempP, tempPri, tempC);
            }else{
                goods[i+1] = new MainGood(tempP, tempPri, tempC);
            }
        }
        
        int[][] dp = new int[n+1][money+1];
        for(int i=1;i<=n;i++){
            for(int j=1;j<=money;j++){
                if(goods[i].code>0){
                    dp[i][j] = dp[i-1][j];
                }else{
                    int totalPrice0, totalPrice1, totalPrice2, totalPrice3, v0, v1, v2, v3;
                    dp[i][j] = dp[i-1][j];
                    totalPrice0 = goods[i].price;
                    v0 = totalPrice0 * goods[i].priority;
                    totalPrice1 = totalPrice0 + goods[goods[i].subCode1].price;
                    v1 = v0+goods[goods[i].subCode1].price*goods[goods[i].subCode1].priority;
                    totalPrice2 = totalPrice0+goods[goods[i].subCode2].price;
                    v2 = v0+goods[goods[i].subCode2].price*goods[goods[i].subCode2].priority;
                    totalPrice3 = totalPrice0 + goods[goods[i].subCode1].price + goods[goods[i].subCode2].price;
                     v3 = v0+goods[goods[i].subCode1].price*goods[goods[i].subCode1].priority+goods[goods[i].subCode2].price*goods[goods[i].subCode2].priority;
                    if(totalPrice0<=j) dp[i][j] = Math.max(dp[i][j], dp[i-1][j-totalPrice0]+v0);
                    if(totalPrice1<=j) dp[i][j] = Math.max(dp[i][j], dp[i-1][j-totalPrice1]+v1);
                    if(totalPrice2<=j) dp[i][j] = Math.max(dp[i][j], dp[i-1][j-totalPrice2]+v2);
                    if(totalPrice3<=j) dp[i][j] = Math.max(dp[i][j], dp[i-1][j-totalPrice3]+v3);
                }
            }
        }
        System.out.println(dp[n][money]);
    }
}

class MainGood{
    
    int price;
    int priority;
    int code; //大于0就是附件
    int subCode1;
    int subCode2;
    
    MainGood(int price, int priority, int code){
        this.price = price;
        this.priority = priority;
        this.code = code;
    }
    
    void setSubCode1(int code1){
        this.subCode1 = code1;
    }
    
    void setSubCode2(int code2){
        this.subCode2 = code2;
    }
    
}

2.简单错误记录
开发一个简单错误记录功能小模块,能够记录出错的代码所在的文件名称和行号。

处理:

1、 记录最多8条错误记录,循环记录,最后只用输出最后出现的八条错误记录。对相同的错误记录只记录一条,但是错误计数增加。最后一个斜杠后面的带后缀名的部分(保留最后16位)和行号完全匹配的记录才做算是”相同“的错误记录。
2、 超过16个字符的文件名称,只记录文件的最后有效16个字符;
3、 输入的文件可能带路径,记录文件名称不能带路径。
4、循环记录时,只以第一次出现的顺序为准,后面重复的不会更新它的出现时间,仍以第一次为准

输入描述:
每组只包含一个测试用例。一个测试用例包含一行或多行字符串。每行包括带路径文件名称,行号,以空格隔开。

输出描述:
将所有的记录统计并将结果输出,格式:文件名 代码行数 数目,一个空格隔开,如:

示例1
输入
复制
D:\zwtymj\xccb\ljj\cqzlyaszjvlsjmkwoqijggmybr 645
E:\je\rzuwnjvnuz 633
C:\km\tgjwpb\gy\atl 637
F:\weioj\hadd\connsh\rwyfvzsopsuiqjnr 647
E:\ns\mfwj\wqkoki\eez 648
D:\cfmwafhhgeyawnool 649
E:\czt\opwip\osnll\c 637
G:\nt\f 633
F:\fop\ywzqaop 631
F:\yay\jc\ywzqaop 631
输出
复制
rzuwnjvnuz 633 1
atl 637 1
rwyfvzsopsuiqjnr 647 1
eez 648 1
fmwafhhgeyawnool 649 1
c 637 1
f 633 1
ywzqaop 631 2

注意:这里有个坑,重复的错误可能在最开头,不能只记录最后的八条,不然就会把开头的这个错误又刷新到最后八条里。

import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;

public class Main{
    public static void main(String[] args){
        List<OneErr> result = new ArrayList<OneErr>();
        Scanner sc = new Scanner(System.in);
        while(sc.hasNextLine()){
            String oneLine = sc.nextLine();
            String[] l = oneLine.split(" ");
            String path = l[0];
            int line = Integer.parseInt(l[1]);
            int slashPos = path.lastIndexOf('\\');
            path = path.substring(slashPos+1);
            if(path.length()>16) path = path.substring(path.length() - 16);
            boolean found = false;
            for(int i =0;i<result.size();i++){
                if(result.get(i).fileName.equals(path) && result.get(i).lineNum == line){
                    result.get(i).cnt++;
                    found = true;
                    break;
                }
            }
            if(!found){
                                    OneErr oneErr = new OneErr(path, line, 1);
                    result.add(oneErr);
            }

        }
        
                    int j = result.size()>=8?(result.size()-8):0;
            for(;j<result.size();j++)
                System.out.printf("%s %d %d\n", result.get(j).fileName, result.get(j).lineNum, result.get(j).cnt);
        
    }
   
}

    class OneErr{
        String fileName;
        int lineNum;
        int cnt;
        public OneErr(String f, int l, int c){
            this.fileName = f;
            this.lineNum = l;
            this.cnt = c;
        }
    }

3.合唱队
题目描述
计算最少出列多少位同学,使得剩下的同学排成合唱队形

说明:

N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足存在i(1<=i<=K)使得T1<T2<…<Ti-1Ti+1>…>TK。

你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

注意不允许改变队列元素的先后顺序
请注意处理多组输入输出!

输入描述:
整数N

输出描述:
最少需要几位同学出列

示例1
输入
8
186 186 150 200 160 130 197 200
输出
4

注意:
1.先递增后递减的一个子列(不是连续子串),重点就是不是连续的;
2.最后输出的是去除的人数,不是留在队伍中的人数;
3.方法就是求一个非连续最长递增子列,然后反过来来一遍,再两两相加减去一,得到最长递增递减非连续子列,然后总长度减去这个长度。

import java.util.Scanner;

public class Main{
    public static void main(String[] args){
        int n;
        Scanner sc = new Scanner(System.in);
        while(sc.hasNextInt()){
            n = sc.nextInt();
            int[] arr = new int[n];
            int[] dpLR = new int[n];
            int[] dpRL = new int[n];
            for(int i=0;i<n;i++){
                int temp = sc.nextInt();
                arr[i] = temp;
                dpLR[i] = 1;
                for(int j=0;j<i;j++){
                    if(arr[i]>arr[j]) dpLR[i] = Math.max(dpLR[i], dpLR[j]+1);
                }
            }
            for(int i=n-1;i>=0;i--){
                dpRL[i] = 1;
                for(int k = i+1;k<n;k++){
                    if(arr[k]<arr[i]) dpRL[i] = Math.max(dpRL[i], dpRL[k]+1);
                }
            }
            int result = 1;
            for(int i=0;i<n;i++){
                if(dpLR[i]+dpRL[i] - 1>result) result = dpLR[i]+dpRL[i]-1;
            }
            System.out.println(n - result);
        }
    }
}

4.前序遍历和中序遍历确定一棵二叉树
题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
示例1
输入
复制
[1,2,3,4,5,6,7],[3,2,4,1,6,5,7]
返回值
复制
{1,2,5,3,4,6,7}

注意:
1.子树只有一个节点或者pr-pl<0的场景要考虑到。

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
//1.用前序遍历和中序遍历重构二叉树;2.用层序遍历储存到数组中
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        int len = pre.length;
        TreeNode root = generateBinaryTree(pre, in, 0, len-1, 0, len-1);
        return root;
    }
    
    public TreeNode generateBinaryTree(int[] pre, int[] in, int pl, int pr, int il, int ir){
        if(pr - pl<0 || ir - il<0) return null;
        if(pl == pr) return new TreeNode(pre[pl]);
        int val = pre[pl];
        int rootIdx = getIdxInOrderTraversal(val, in);
        TreeNode root = new TreeNode(val);
        //递归生成左子树
        int pll, plr, ill, ilr, prl, prr, irl, irr;
        pll = pl+1;
        ilr = rootIdx - 1;
        ill = il;
        plr = pll + (ilr - ill);
        root.left = generateBinaryTree(pre, in, pll, plr, ill, ilr);
        
        //递归生成右子树
        prr = pr;
        irl = rootIdx + 1;
        irr = ir;
        prl = prr - (irr - irl);
        root.right = generateBinaryTree(pre, in, prl, prr, irl, irr);
        return root;
    }
    
    private int getIdxInOrderTraversal(int value, int[] in){
        for(int i=0;i<in.length;i++)
            if(value == in[i]) return i;
        return -1;
    }
    
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值