算法题目集

1、来自网易:
为了找到自己满意的工作,牛牛收集了每种工作的难度和报酬。牛牛选工作的标准是在难度不超过自身能力值的情况下,牛牛选择报酬最高的工作。在牛牛选定了自己的工作后,牛牛的小伙伴们来找牛牛帮忙选工作,牛牛依然使用自己的标准来帮助小伙伴们。牛牛的小伙伴太多了,于是他只好把这个任务交给了你。
思路:按照工作难度进行排序,这里用到了Arrays.sort(),重写了compare方法。再按照dp的方法,每次比较和前一项工作获利最大的填入map。最后直接取map.floorKey()(方法调用返回的最大键小于等于key,或者null,如果不存在这样的键)

import java.util.*;
public class Main{
 ublic static void main(String[] args) {
        // TODO Auto-generated method stub
         Scanner sc=new Scanner(System.in);
         TreeMap map=new TreeMap<>();
         int m=sc.nextInt();//工作的数量
         int n=sc.nextInt();//小伙伴的数量
         int[][] work=new int[m][2];
         for(int i=0;i() {
             public int compare(int[] num1,int[] num2){
                 return num1[0]-num2[0];
             }
        });
         //dp使相应的工作难度获利最大,并把结果存入map中
         for(int i=1;i

2、题目:整数成绩最大化
给出一个整数n,将n分解为至少两个整数之和,使得这些整数的乘积最大化,输出能够获得的最大的乘积。

例如:
2=1+1,输出1;
10=3+3+4,输出36。
输入为1个整数,输出为1个整数

示例1:输入:10,输出:36

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
       //2和3单独处理
        if(n==2){
            System.out.println(1);
            return;
        }
        if(n==3){
            System.out.println(2);
            return;
        }
        System.out.println(getResult(n));
    }
    //最优化问题,尽量都分成3,不足部分就分成2
    static long getResult(int n){
        long result=1;
        while(n>4){
            result=result*3;
            n-=3;
        }
        result = result*n;
        return result; 
    }
}

3、题目:寻找合法字符串
给出一个正整数n,请给出所有的包含n个'('和n个')'的字符串,使得'('和')'可以完全匹配。
例如:
'(())()','()()()' 都是合法的;
'())()('是不合法的。
请按照字典序给出所有合法的字符串。

示例1:输入:2 输出:(()),()()

由于字符串只有左括号和右括号两种字符,而且最终结果必定是左括号3个,右括号3个,所以我们定义两个变量left和right分别表示剩余左右括号的个数,如果在某次递归时,左括号的个数大于右括号的个数,说明此时生成的字符串中右括号的个数大于左括号的个数,即会出现')('这样的非法串,所以这种情况直接返回,不继续处理。如果left和right都为0,则说明此时生成的字符串已有3个左括号和3个右括号,且字符串合法,则存入结果中后返回。如果以上两种情况都不满足,若此时left大于0,则调用递归函数,注意参数的更新,若right大于0,则调用递归函数,同样要更新参数。代码如下:

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        List list=new ArrayList<>();
        DFS(n,n,"",list);
        Collections.sort(list);
        for(int i=0;i list){
        if(left<0||right<0||left>right){
            return;
        }
        if(left==0&&right==0){
            list.add(result);
            return;
        }
        DFS(left-1,right,result+"(",list);
        DFS(left,right-1,result+")",list);
        
    }
}

引申:全排列的递归算法
算法思路:

(1)n个元素的全排列=(n-1个元素的全排列)+(另一个元素作为前缀);

(2)出口:如果只有一个元素的全排列,则说明已经排完,则输出数组;

(3)不断将每个元素放作第一个元素,然后将这个元素作为前缀,并将其余元素继续全排列,等到出口,出口出去后还需要还原数组;

4、Map:

Map map=new Hashmap<>();
 (1)int t=map.get(str);
(2)  for(String key:map.keySet()){
             if(str.equals(key)){
                       int i=map.get(key);
                       map.put(str,i+1);
              }else{
                    map.put(str,1);
              }
     }
        

6、题目:
给定一个整型数组a (int[]),包含N个元素。判断是否这N个整数可以构造出一个 "按位与等式",即是否存在这样一个等式:将数组中的任意N-1个整数进行按位求与操作(即Java中的"&"操作),得到的结果等于剩下的那个整数,注意这N个整数在等式中均必须出现且只出现一次。举例一:给定一个数组[5,20,4],结果为:true,因为 "20 & 5 = 4"。举例二:[5,3,7,19],结果为:false,因为数组中任何三个整数按位取与,均无法等于剩下的那个整数。请按如下函数定义,写出你的程序。(注:不能使用本地IDE)

boolean isAndEqationExist(int[] a);

分析:一个重要的点是,等号右边的数字的特征:因为是求与的结果,所以必然是最小的数字。如果能分析到这个点,则只需要一次求与的尝试,即可得出结论。
即每次都遍历找最小值,同时求它们相与的结果,(一个数与自己是不变的),结果等于最小值,则返回true;

boolean isAndEqationExist(int[] a) {
    if (a == null || a.length == 0)
        return false;
    int s = a[0];
    int min = s;
    for (int i = 1; i < a.length; i++) {
        s &= a[i];
        if (min > a[i])
            min = a[i];
    } 
    if (min == s)
        return true;
    return false;  
}

7、
动态规划!!!
当求解的问题满足以下两个条件时, 就应该使用动态规划:
(1)主问题的答案 包含了 可分解的子问题答案 (也就是说,问题可以被递归的思想求解)
(2)递归求解时, 很多子问题的答案会被多次重复利用

动态规划的本质思想就是递归, 但如果直接应用递归方法, 子问题的答案会被重复计算产生浪费, 同时递归更加耗费栈内存(具体为什么更加消耗栈内存, 需要额外了解函数调用过程中, 进程栈内存的管理方式),所以通常用一个二维矩阵(表格)来保存不同子问题的答案, 避免重复计算

例题:合唱团(2016网易)
有 n 个学生站成一排,每个学生有一个能力值,从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能力值的乘积最大,返回最大的乘积。 每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每个学生的能力值 ai(-50 <= ai <= 50)。接下来的一行包含两个整数,k 和 d (1 <= k <= 10, 1 <= d <= 50)。
思路:
因为有正有负,负负得正,所以要维护两个dp数组,一个存储最大,一个存储最小。
定义fmax[k][i]表示当选中了k个学生,并且以第i个学生为结尾,所产生的最大乘积;fmin[k][i]表示 当选中了k个学生,并且以第i个学生为结尾,所产生的最小乘积;
当前的最大fmax[k+1][i+1]应该是前面产生的最大乘积当前学生的能力值。那么fmax[k+1][i+1]=max(fmax[k][i]stu[i+1],fmin[k][i]stu[i+1]),即当选中了k个学生后,再选择第i+1编号学生,所产生的最大乘积;
同理fmin[k+1][i+1]=min(fmin[k][i]
stu[i+1],fmax[k][i]*stu[i+1])。
最后,遍历一遍fmax[K][i]求得最大值(i从1~N)。

public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        while(sc.hasNextInt()){
        int n=sc.nextInt();
        int arr[]=new int[n+1];
        for(int i=1;i<=n;i++){
             arr[i]=sc.nextInt();
        }
        
        int k=sc.nextInt();
        int d=sc.nextInt();
        long[][] fmax=new long[k+1][n+1];//记录最大乘积
        long[][] fmin=new long[k+1][n+1];//记录最小乘积
        // fmax[k][i]表示 : 当选中了k个学生,并且以第i个学生为结尾,所产生的最大乘积;
        // fmin[k][i]表示 : 当选中了k个学生,并且以第i个学生为结尾,所产生的最小乘积;
        long result=Integer.MIN_VALUE;
        for(int i=1;i<=n;i++){
           fmax[1][i]=arr[i];
           fmin[1][i]=arr[i];
            for(int m=2;m<=k;m++){//m是选中的学生数,要构造选中序列,最多选中k个学生
                for(int j=i-1;j>0&&i-j<=d;j--){//j是从第i位往前,控制序列的位置范围不超过d
                   fmax[m][i]=Math.max(fmax[m][i],Math.max(fmax[m-1][j]*arr[i],fmin[m-1][j]*arr[i]));
//fmax[m][i]记录下每个i对应的最大乘积,每次要先和上次的最大乘积还有这次往前增加了一位之后的位置fmax[m-1][j](m-1是因为如果m=2,就是0或者1)或者fmin[m-1][j]与当前值arr[i]的乘积的最大值,再做比较求最大
                   fmin[m][i]=Math.min(fmin[m][i],Math.min(fmax[m-1][j]*arr[i],fmin[m-1][j]*arr[i]));
//最小值和最大值道理一样
                }
            }
            result=Math.max(result,fmax[k][i]);//相当于递归找出最大的乘积
        } 
        System.out.println(result);
        }   
    }
}

你可能感兴趣的:(算法题目集)