算法基础

基础

与、或、异或

  1. 与&:同1为1 。 与1就是保留,与0就是清楚(屏蔽)
  2. 或|:有1为1 。
  3. 异或(不进位加法):同为0异为1 。 用来消去重复,异或0就是保留
  4. x&(x-1)就是将x二进制中最低位的一置为0
 //统计二进制中1的个数
 static int numberOf1(int x){
        int count=0;
	     while (x!=0){
	         x&=x-1;
   	      count++;
  	   }
  	   return count;
 }
  1. x二进制中奇偶位互换
static int f(int i){
       int ou=i&0xaaaaaaaa;//1010,偶数位置为0,异或0为清除
       int ji=i&0x55555555;//0101,奇数位置为0
       return (ji<<1)^(ou>>1);
   }
  1. 用二进制表示的小数
static String decimalByBinary(double i){
       //0.625表示成0.101(0.5+0.25)
       // //整数转二进制除二,小数转二进制乘二
      String s="0.";
      while (i>0){
           double num=i*2;
           if (num>=1){
               i=num-1;
               s+="1";
           }
           else {
               i=num;
               s+="0";
           }
           if (s.length()>34)//小数点后32位不能表示的
               return "ERROR";
      }
       return s;

   }

简单的递归

/**
    * 走39级台阶,每次走一步或者两步,有多少种走法
    * @param n
    * @param steps
    * @return
    */
   private static int f(int n, int steps) {
       if (n<0)return 0;
       if (n==0)return 1;
       return f(n,steps-1)+f(n,steps-2);
   }
/** 用递归实现插入排序
     * 对前k-1个元素排序
     * 把位置k的元素插入到前面的部分
     * @param arr 
     * @param k 数组最后一个元素下标
     * @return
     */
    private static int[] insertSort(int[] arr, int k) {
        if (k==0) return arr;
        insertSort(arr,k-1);
        int t=arr[k];
        int index=k-1;
        while (index>=0&&arr[index]>t){
            arr[index+1]=arr[index];
            index--;//1,2,6要插4:    6后移一位,1,2, ,6 同时index--,所以要在index+1位置上插入
        }

        arr[index+1]=t;//最后index
        return arr;
    }

用递归实现插入排序

/** 用递归实现插入排序
    * 对前k-1个元素排序
    * 把位置k的元素插入到前面的部分
    * @param arr 
    * @param k 数组最后一个元素下标
    * @return
    */
   private static int[] insertSort(int[] arr, int k) {
       if (k==0) return arr;
       insertSort(arr,k-1);
       int t=arr[k];
       int index=k-1;
       while (index>=0&&arr[index]>t){
           arr[index+1]=arr[index];
           index--;//1,2,6要插4:    6后移一位,1,2, ,6 同时index--,所以要在index+1位置上插入
       }

       arr[index+1]=t;//最后index
       return arr;
   }

打印汉诺塔问题路径

/**打印汉诺塔问题路径
    *
    * @param n 初始的N个从小到达的盘子,N是最大编号
    * @param form
    * @param to
    * @param help
    */
   private static void printHanoiTower(int n, String form, String to, String help) {
       if (n==1){
           System.out.println(" move "+n+" form "+form+" to "+to);
           return;
       }
       printHanoiTower(n-1,form,help,to);  //先把前N-1个盘子挪到辅助空间上去
       System.out.println(" move "+n+" form "+form+" to "+to);//N可以顺利到达to
       printHanoiTower(n-1,help,to,form);  // 让N-1从辅助空间回到目标空间上去


   }

查找与排序

用递归实现二分查找

private static int binarySearch1(int[]a, int form, int to, int key) {
        if (form>to)
            return -1;
        int mid=(form+to)/2;
        int midval=a[mid];
        //也可以写成while(form<=to)而不用递归
        if (midval<key)
            return binarySearch1(a,mid+1,to,key);
        else if (midval>key)
                return binarySearch1(a,form,mid-1,key);
            else
                return mid;//写成三个if 编译会不通过
    }
    int[] a = {1, 6, 5, 9};
    System.out.println(binarySearch1(a,0,3,5));

希尔排序

private static int[] shellSort(int[] a) {
       for (int interval = a.length/2; interval >0 ; interval/=2) {//不断缩小增量
           //1,6,5,9分两组,15一组,69一组,组内做插入排序
           for (int i = interval; i <a.length ; i++) {
               int target=a[i];//要插入的数
               int index=i-interval;
               while (index>=0&&a[index]>target){
                   a[index+interval]=a[index];
                   index-=interval;
               }
               a[index+interval]=target;
           }
       }
       return a;
   }
   int[] a = {1, 6, 5, 9};
   

算法复杂度分析

  • 斐波拉切递归的算法复杂度为2n
  • gcd递归的时间复杂度为lg(n)

/**
     * 时间复杂度约为lg(n)每次减半
     * @param a
     * @param n
     * @return
     */
    private static int pow(int a, int n) {
        int res=a;
        if (n==0)return 1;
        if (n==1)return res;
        int e=1;
        while (e*2<=n){
            res*=res;
            e*=2;
        }
        return res*pow(a,n-e);
    }

快速排序

package 算法基础;

/**
 * @author 欧阳煜
 * @date 2019/7/31 16:54
 */
public class 快排 {
    public static void main(String[] args) {
        int[]a={1,3,2,9,5};

        for(int i:a)
            System.out.print(i+" ");
        quickSort(a,0,a.length-1);
        System.out.println();
        for(int i:a)
            System.out.print(i+" ");
    }

    private static void quickSort(int[] a, int begin, int end) {
        if (begin<end){
            int midIndex=partition2(a,begin,end);
            quickSort(a,begin,midIndex-1);
            quickSort(a,midIndex+1,end);
        }
    }

    private static int partition2(int[] a, int begin, int end) {
        //双向扫描分区
        int pivot=a[begin];
        int left=begin+1;
        int right=end;
        while (left<right){
            while (left<=right&&a[left]<=pivot)
                left++;
            while (left<=right&&a[right]>pivot)
                right--;
            if (left<right) {//当left不往前走即left指向了一个大的
                int t = a[left];
                a[left] = a[right];
                a[right] = t;
            }
        }
        int t=a[begin];
        a[begin]=a[right];
        a[right]=t;
        return right;
    }

    private static int partition(int[] a, int begin, int end) {
        //单向扫描分区
        int pivot=a[begin];
        int left=begin+1;
        int right=end;
        while (left<=right){
            if (a[left]<=pivot)
                left++;
            else {
                int t=a[left];
                a[left]=a[right];
                a[right]=t;
                //扫描元素大于主元,交换左右指针的元素,右指针左移
                right--;
            }
        }
        int t=a[begin];
        a[begin]=a[right];
        a[right]=t;//把主元元素移到中间,下标为right

        return right;

    }

}


归并排序

package 算法基础;

/**
 * @author 欧阳煜
 * @date 2019/7/31 20:02
 */
public class 归并排序 {
    public void mergeSort(int[]a,int begin,int end){
        if(begin<end){
            int midIndex=(begin+end)/2;
            mergeSort(a,begin,midIndex-1);//左边排好序
            mergeSort(a,midIndex+1,end);//右边排好序
            merge(a,begin,midIndex,end);//合并两边的有序

        }
    }
    public void merge(int []a,int begin,int midIndex,int end){
        int []tmp=new int[a.length];//辅助数组
        int p1=begin,p2=midIndex+1,k=begin;//p1、p2是检测指针,k是存放指针

        while(p1<=midIndex && p2<=end){
            if(a[p1]<=a[p2])
                tmp[k++]=a[p1++];
            else
                tmp[k++]=a[p2++];
        }

        while(p1<=midIndex)
            tmp[k++]=a[p1++];//如果第一个序列未检测完,直接将后面所有元素加到合并的序列中
        while(p2<=end)
            tmp[k++]=a[p2++];//同上

        //复制回原素组
        for (int i = begin; i <=end; i++)
            a[i]=tmp[i];
    }

}

找数组第k小的元素

package 算法基础;
/**
* @author 欧阳煜
* @date 2019/8/1 8:49
*/
public class 找数组第k小的元素 {
   //O(n)
   public static int selectK (int[]a,int begin,int end,int k){
       int pivotIndex=快排.partition2(a,begin,end);
       int pivotIndexK=pivotIndex-begin+1;//主元在数组中是第几小的元素
       if (pivotIndexK==k) return a[pivotIndex];//返回主元
       else if (pivotIndexK>k)//是第6小  大于  要找的第4小
               return selectK(a,begin,pivotIndex-1,k); //在左边找第4小
            else //第2小   小于   要找的第4小
                return selectK(a,pivotIndex+1,end,k-pivotIndexK);//在右边找第二小
   }



   public static void main(String[] args) {
       int[]a={3,9,7,6,1,2};
       System.out.println(selectK(a,0,a.length-1,4));
   }
}

数组中出现次数超过半数的元素

package 算法基础;

/**
* @author 欧阳煜
* @date 2019/8/1 11:07
*/
public class 哪个数字超过了一半 {
   public static void main(String[] args) {
       System.out.println(moreThanHalf(new int[]{3,5,6,8,8,8,8}));
   }

   private static int moreThanHalf(int[] a) {
       int candidate=a[0];
       int times=1;//出现的次数
       for (int i = 1; i < a.length; i++) {//扫描数组
           if (times==0){
               candidate=a[i];
               times=1;
               continue;
           }
           if(a[i]==candidate){
               times++;//遇到和候选值相同的,次数加1
           }
           else
               times--;//不同的数,进行消减
       }
       return candidate;
   }
}

最小未出现的数字

package 算法基础;

/**
 * @author 欧阳煜
 * @date 2019/8/1 12:07
 */
public class 最小未出现的数字 {
    public static void main(String[] args) {
        int[] a = {1, 2, 3, 4, 5, 8, 9, 10, 11, 10000};
        //6
        System.out.println(find1(a,0,a.length-1));
        System.out.println(find2(a));
    }

    /**
     * 利用辅助空间
     * @param a
     * @return
     */
    private static int find2(int[] a) {
        int n=a.length;
        int helper[]=new int[n+1];
        for (int i = 0; i < n; i++) {
            if (a[i]<n+1)
                helper[a[i]]=1;
        }
        for (int i = 1; i < n+1; i++) {//下标为1,表示原数组中1这个元素有没有出现
            if (helper[i] == 0) {
                return i;
            }
        }
        return n+1;//数组从1开始紧密,返回n+1
    }

    /***
     * 利用递归
     * @param a
     * @return
     */
    private static int find1(int[] a,int left,int right) {
        if (left>right)
            return left+1;
        int midIndex=(left+right)/2;
        int mid=找数组第k小的元素.selectK(a,left,right,midIndex-left+1);//实际在中间位置的值3
        //1,2,4,6,7
        int qi=midIndex+1;//期望值4
        if (mid==qi)//左侧紧密
            return find1(a,midIndex+1,right);
        else //左侧稀疏
            return find1(a,left,midIndex-1);

    }

}

数组的包含问题

public class 数组的包含问题 {
    public static void main(String[] args) {
        System.out.println(contains("abc","bc"));
        System.out.println("l".indexOf("f")>-1);
        System.out.println(contains("abc","bc"));
        System.out.println(contains1("abc","bc"));
    }

    /**
     * 前n²,后n*lg(N)
     * @param a
     * @param b
     * @return
     */
    public static boolean contains(String a,String b){
        for (int i = 0; i < b.length(); i++) {//a中找b,遍历b
            if (a.indexOf(b.charAt(i))==-1){
                return false;
            }
        }
        return true;
    }
    public static boolean contains1(String a,String b){
        char[]chars=a.toCharArray();
        Arrays.sort(chars);
        for (int i = 0; i < b.length(); i++) {
            char t=b.charAt(i);
            int index = Arrays.binarySearch(chars, t);
            if (index==-1)
                return false;
        }
        return true;
    }
}

数学问题

扩展欧几里得算法求解线性方程

package 算法基础.数学问题;

/**
 * @author 欧阳煜
 * @date 2019/8/7 20:26
 */
public class 扩展欧几里得算法 {
    /**
     *
     */
    static long x,y;

    public static void main(String[] args) {
        linerEquation(12,42,6);
        //long d = extGcd(12, 42);

    }



    /**
     * 调用完成后,xy是ax+by=gcd(a,b)的x大于0的第一个解
     * @return
     */
    public static long extGcd(long a,long b){
        if (b==0){
            x=1;
            y=0;
            return a;
        }
        long res=extGcd(b,a%b);
        //x,y已经被下一层递归更新了,所以要备份
        long x1=x;//备份x,
        x=y;//更新x
        y=x1-a/b*y;//更新y
        
        return res;
    }

    /**
     *
    ax+by=m  当m=gcd(a,b)的倍数时有整数解
     12x+42y=6,求出来解(-3,1),可推算出通解
     x=x+b/d=-3+7=4  或减
     y=y-a/d=1-2=-1
     */
    public static void linerEquation(long a,long b ,long m) {
        long gcd = extGcd(a, b);
        if (m%gcd!=0) {System.out.println("No Answer");return;}
        x*=m/gcd;
        y*=m/gcd;
        
    }
}

在这里插入图片描述

由分析可知:
x+mk与y+nk同余L(k表示相遇时跳的次数,L表示圈的长度)(余数相同叫同余)
可得(m-n)k+L*t=y-x
求最小的k
用拓展欧几里得x,再求通解里>0里最小的

常见求和公式

     *1~n的平方和:n(n+1)(2n+1)/6
     * 1~n的立方和:(n+1)/4
     * 几何级数:x的0到n次方相加:(x.pow(n+1)-1)/(x-1)
     *         20到n次方相加:2.pow(n+1)-1
     *         if(abs(x)<1  :1/(1-x)
     * 调和级数1+1/2+1/3+...+1/n:约为ln(n)
     */ ```

同余方程中的逆元

/**
     *
     * ax%n=1,就说a关于n的逆元为x
     * 即ax+ny=1
     * 当gcd(a,n)=1才有解
     * 特殊的,ax=1,就说a的逆元为x
     *
     */
    /*
    已知a%9973和b,求(a/b)%9973
    :
    记b关于9973的逆元为x
    化为(a*x)%9973
    即(a%9973 * x)%9973
     */
    public static long inverseElement(long a,long mo){
        linerEquation(a,mo,1);
        x=((x%mo)+mo)%mo;//保证x>0
        return x;
    }

质数分解

package 算法基础.数学问题;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class 质因数分解 {
    public static void main(String[] args) {
        List<Integer> list=primeFactor(100);
        String s="";
        for(Integer i:list){
            s+="*"+i;
        }
        System.out.println(s.substring(1));
    }

    public static List<Integer> primeFactor(int num){
        List<Integer> list=new ArrayList<>();
        for (int i = 2; i *i<=num ; i++)
            while (num%i==0){
                list.add(i);
                num/=i;
            }
        return list;
    }

}

DFS

选若干个数,和为k

package 算法基础.dfs;

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

/**
 * @author 欧阳煜
 * @date 2019/8/27 14:10
 */

/**
 * 给定整数序列a1,a2,...,an,判断是否可以从中选出若干数,使它们的和恰好为k.
 *
 * 1≤n≤20
 *
 * -10^8≤ai≤10^8
 *
 * -10^8≤k≤10^8
 * 样例:
 *
 * 输入
 *
 * n=4
 * a={1,2,4,7}
 * k=13
 * 输出:
 *
 * Yes (13 = 2 + 4 + 7)
 */
public class DFS_部分和 {
    static int kk;
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int[]a=new int[n];
        for (int i = 0; i <n; i++) {
            a[i]=sc.nextInt();
        }
        int k=sc.nextInt();
         kk=k;//最后k一定为0,先拷贝副本
        f(a,k,0,new ArrayList<>());
    }

    private static void f(int[] a, int k, int cur, ArrayList<Integer> list) {

        if (k==0){
            System.out.print("YES("+kk+"=");
            for (int i = 0; i < list.size(); i++) {
                System.out.print(list.get(i)+(i==list.size()-1?"":"+"));
            }
            System.out.println(")");
        }
        if (k<0) return;
        if (cur>a.length-1)return;
        f(a,k,cur+1,list); //不要a[cur]这个元素


        list.add(a[cur]);
        int index=list.size()-1;//a[cur]在list中的下标

        f(a,k-a[cur],cur+1,list); //要a[cur]这个元素,剩余k-a[cur]

        list.remove(index);//回溯
    }
}

n皇后问题

package 算法基础.dfs;

/**
 * @author 欧阳煜
 * @date 2019/8/27 16:41
 */

/**
 *   请设计一种算法,解决著名的n皇后问题。这里的n皇后问题指在一个n*n的棋盘上放置n个皇后, 使得每行每列和每条对角线上都只有一个皇后(即皇后不能在同一行,不能在同一列,也不能在一条斜线上),求其摆放的方法数。
 */
public class n皇后问题_二维数组 {
    public static int count;
    public static void main(String[] args) {
        int[][]a=new int[4][4];
        dfs(a,0,0);
        System.out.println(count);
    }

    private static void dfs(int[][] a, int x, int y) {
        if (x==a.length){
            for (int i = 0; i < a.length; i++) {
                for (int j = 0; j < a[0].length; j++) {
                    System.out.print(a[i][j]+" ");
                }
                System.out.println();
            }
            count++;
            return;
        }

        for (int i = 0; i < a.length; i++) {  //选一个位置
            if (check(a,x,i)){  //如果可以放
                a[x][i]=1;      //放
                dfs(a,x+1,0);//下一行
                a[x][i]=0; //回溯!!!

            }
        }
    }

    private static boolean check(int[][] a, int x, int y) {
        for (int i = 0; i < a.length; i++) {
            for (int j = 0; j < a[0].length; j++) {
                if (i==x&&a[i][j]==1) return false;
                if (j==y&&a[i][j]==1) return false;
                if (x+y==i+j&&a[i][j]==1) return false;
                if (y-x==j-i&&a[i][j]==1) return false;

            }
        }

        return true;
    }

}

困难的串

package 算法基础.dfs;

/**
 * @author 欧阳煜
 * @date 2019/8/27 19:28
 */
/**
 * 问题描述:如果一个字符串包含两个相邻的重复子串,则称它为容易的串,其他串称为困难的串,
 * 如:BB,ABCDACABCAB,ABCDABCD都是容易的,A,AB,ABA,D,DC,ABDAB,CBABCBA都是困难的。

 输入正整数n,L,输出由前L个字符(大写英文字母)组成的,字典序第n小的困难的串。
 例如,当L=3时,前7个困难的串分别为:
 A,AB,ABA,ABAC,ABACA,ABACAB,ABACABA
 n指定为4的话,输出ABAC
 */
public class 困难的串 {
    public static int count;
    public static void main(String[] args) {
        int n=4;
        int l=3;
        dfs(l,n,"");
    }

    private static void dfs(int l, int n, String prefix) {

        if (count==4){
            System.out.println(prefix);
            System.exit(0);
        }
        for (char i ='A'; i <'A'+l ; i++) {//尝试在prefix后追加一个字符
           // if (isHard(prefix,i)){
            if (isHard(prefix+i)){
                System.out.println(prefix+i);
                count++;
                dfs(l,n,prefix+i);
            }
        }
    }

    private static boolean isHard(String s) {
        s=new String( new StringBuilder(s).reverse());
        for (int i = 1; i <= s.length()/2; i++) {//写for循坏千万要注意等不等于的问题
            String s1=s.substring(0,i);
            String s2=s.substring(i,2*i);
            if (s1.equals(s2))
                return false;
        }
        return true;
    }

}

数独

package 算法基础.dfs;

/**
 * @author 欧阳煜
 * @date 2019/8/27 12:57
 */

import java.util.Scanner;


/**
 * 你一定听说过“数独”游戏。 如下图所示,玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个同色九宫内的数字均含1-9,不重复。 数独的答案都是唯一的,所以,多个解也称为无解。 本图的数字据说是芬兰数学家花了3个月的时间设计出来的较难的题目。但对会使用计算机编程的你来说,恐怕易如反掌了。 本题的要求就是输入数独题目,程序输出数独的唯一解。我们保证所有已知数据的格式都是合法的,并且题目有唯一的解。 格式要求,输入9行,每行9个数字,0代表未知,其它数字为已知。 输出9行,每行9个数字表示数独的解。
 */
public class 数独求解_DFS {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        char[][] a = new char[9][9];

        for (int i = 0; i < 9; i++) {
            a[i] = sc.nextLine().toCharArray();
        }

        dfs(a, 0, 0);
        
    }

    private static void dfs(char[][] a, int x, int y) {
        if (x == 8 && y == 8) {//退出的条件
            for (int i = 0; i < 9; i++) {
                System.out.println(new String(a[i]));
            }

            System.exit(0);//不用return,return还会找其他路,题意有唯一解,找到就退走程序
        }

        if (a[x][y] == '0') {//如果要填
            for (int i = 1; i < 10; i++) { //选一个
                if (check(a, x, y, i)) {  //如果可以填
                    a[x][y] = (char) ('0' + i);   //填上
                    dfs(a, x + (y + 1) / 9, (y + 1) % 9);  //下一位
                }
            }
            a[x][y] = '0';  //回溯
        } else //已经有了数字
            dfs(a, x + (y + 1) / 9, (y + 1) % 9); //下一位

    }

    private static boolean check(char[][] a, int x, int y, int k) {
        for (int i = 0; i < 9; i++) {
            if (a[i][y] == (char) ('0' + k))
                return false;
            if (a[x][i] == (char) ('0' + k))
                return false;

            for (int j = x / 3 * 3; j < (x/ 3+ 1) * 3; j++) { // 所在小九宫格起始行
                for (int l = y / 3 * 3; l < (y/ 3+ 1) * 3; l++) {// 所在小九宫格起始列
                    if (a[j][l]==(char) ('0' + k))
                        return false;
                }
            }

        }
        return true;
    }


}

最大公共子序列

package 算法基础.dfs;
import java.util.ArrayList;

/**
 * @author 欧阳煜
 * @date 2019/8/31 9:54
 */
public class 最大公共子序列 {
    public static void main(String[] args) {
        System.out.println(dfs("3563243", "513141"));

    }
    static ArrayList<Character> dfs(String s1, String s2) {
        int len1 = s1.length();
        int len2 = s2.length();
        ArrayList<Character> ans = new ArrayList<>();
        for (int i = 0; i < len1; i++) {
            //求以i字符开头的公共子序列
            ArrayList<Character> list = new ArrayList<>();
            //和s2的每个字符比较
            for (int j = 0; j < len2; j++) {
                if (s1.charAt(i) == s2.charAt(j)) {//如果相同
                    list.add(s1.charAt(i));
                    list.addAll(dfs(s1.substring(i + 1), s2.substring(j + 1)));
                    break;
                }
            }
            if (list.size() > ans.size()) {
                ans = list;
            }
        }
        return ans;
    }
}

素数环

package 算法基础.dfs;

/**
 * @author 欧阳煜
 * @date 2019/8/27 18:55
 */
/**
 * 输入正整数n,对1-n进行排列,使得相邻两个数之和均为素数,
 * 输出时从整数1开始,逆时针排列。同一个环应恰好输出一次。
 * n<=16
 *
 * 如输入:6
 * 输出:
 * 1 4 3 2 5 6
 * 1 6 5 2 3 4
 */

public class 素数环 {
    public static void main(String[] args) {
        int n=6;
        int []a=new int[6];
        a[0]=1;
        dfs(a,1);
    }

    private static void dfs(int[] a, int cur) {
        if (cur==a.length&&isP(a[0]+a[a.length-1])){//填到末尾了,还有首尾相加为素数才算成功
            for (int i = 0; i < a.length; i++) {
                System.out.print(a[i]+" ");
            }
            System.out.println();
            return;
        }
        for (int i = 2; i <=a.length; i++) {
            if (check(a,i,cur)){//a中没有i这个数,且和上一个数之和为素数
                a[cur]=i;
                dfs(a,cur+1);
                a[cur]=0;//回溯
            }
        }
    }

    private static boolean check(int[] a, int i, int cur) {
        for (int e:a)
            if (e==i||!isP(a[cur-1]+i))
                return false;

        return true;
    }

    private static boolean isP(int i) {
        for (int j = 2; j <=Math.sqrt(i) ; j++) {
            if (i%j==0)return false;
        }
        return true;
    }
}

贪心

不相交区间

package 算法基础.贪心;



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

/**
 * @author 欧阳煜
 * @date 2019/8/29 17:36
 */
/*
有n项工作,每项工作分别在si时间开始,在ti时间结束.

对于每项工作,你都可以选择参与与否.如果选择了参与,那么自始至终都必须全程参与.

此外,参与工作的时间段不能重复(即使是开始的瞬间和结束的瞬间的重叠也是不允许的).

你的目标是参与尽可能多的工作,那么最多能参与多少项工作呢?

1≤n≤100000

1≤si≤ti≤10^9

输入:

第一行:n
第二行:n个整数空格隔开,代表n个工作的开始时间
第三行:n个整数空格隔开,代表n个工作的结束时间
样例输入:
5
1 3 1 6 8
3 5 2 9 10

样例输出:

3

说明:选取工作1,3,5
 */
public class 不相交区间 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int[]s=new int[n];
        int[]e=new int[n];
        for (int i = 0; i <n; i++) {
            s[i]=sc.nextInt();
        }
        for (int i = 0; i <n; i++) {
            e[i]=sc.nextInt();
        }


        Job[] jobs=new Job[n];
        for (int i = 0; i < n; i++) {
            jobs[i]=new Job(s[i],e[i]);
        }

        Arrays.sort(jobs);

        System.out.println(f(n,jobs));


    }

    private static int f(int n, Job[] jobs) {
        int count=1;
        int y=jobs[0].e;//总是选择最先完成的
        for (int i = 0; i < n; i++) {
            if (jobs[i].s > y) {//保证不相交
                count++;
                y=jobs[i].e;//更新最后完成时间
            }
        }
        return count;
    }
    /**
     * 内部类
     * 便于将e[]排序时s[]也跟着变
     *
     */
    public static class Job implements Comparable<Job> {
        int s;
        int e;

        public Job(int s, int e) {
            this.s = s;
            this.e = e;
        }
        @Override
        public int compareTo(Job o) {
            if (this.e==o.e)
                return this.s-o.s;
            return this.e-o.e;
        }
    }
}

字典序最小

package 算法基础.贪心;

import java.util.Scanner;

/**
 * @author 欧阳煜
 * @date 2019/8/29 18:55
 */
/*
字典序最小问题

给一个定长为N的字符串S,构造一个字符串T,长度也为N。

起初,T是一个空串,随后反复进行下列任意操作

1. 从S的头部删除一个字符,加到T的尾部
2. 从S的尾部删除一个字符,加到T的尾部

目标是最后生成的字符串T的字典序尽可能小

1≤N≤2000
字符串S只包含大写英文字母

输入:字符串S
acdbcb
输出:字符串T
abcbcd
POJ - 3617 要求每80个字符换行输出
 */
public class 字典序最小 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        // String s = sc.nextLine();
        formatPrint(f(sc.nextLine()));

        System.out.println();
    }

    private static String f(String s) {
        System.out.println(s.length());
        String ss=new StringBuilder(s).reverse().toString();

        StringBuilder res=new StringBuilder();

        while (res.length()<=s.length()+1){

            if (s.compareTo(ss)<=0){
                res.append(s.charAt(0));
                s=s.substring(1);
            }
            else {
                res.append(ss.charAt(0));
                ss=ss.substring(1);
            }
        }
        return res.toString();
    }
    public static void formatPrint(String s){
        for (int i = 0; i < s.length(); i++) {
            System.out.print(s.charAt(i));
            if ((i+1)%80==0) {
                System.out.println();
            }
        }
    }


}

快速过桥

package 算法基础.贪心;

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

/**
 * @author 欧阳煜
 * @date 2019/8/29 9:30
 */
public class 快速过桥 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int[]speed=new int[n];
        for (int i = 0; i <n; i++) {
            speed[i]=sc.nextInt();
        }
        Arrays.sort(speed);
        System.out.println(f(n,speed));
    }

    private static int f(int n, int[] speed) {
        int ans=0;
        while (n>0){
            if (n==1){
                ans+=speed[0];
                break;
            }
            if (n==2){
                ans+=speed[1];
                break;
            }
            if (n==3){
                ans+=speed[0]+speed[1]+speed[2];
                break;
            }
            if (n>3){
                int s1=speed[0]+speed[1]*2+speed[n-1];//a+3b+d
                int s2=2*speed[0]+speed[n-1]+speed[n-2];//2a+b+c+d
                ans+=Math.min(s1,s2);
                n-=2;
            }
        }
        return ans;
    }
}

最少的硬币数

package 算法基础.贪心;

import java.util.Scanner;

import static java.lang.Math.min;


/**
 * @author 欧阳煜
 * @date 2019/8/28 19:37
 */
/*
硬币问题

有1元,5元,10元,50元,100元,500元的硬币各c1,c5,c10,c50,c100,c500枚.

现在要用这些硬币来支付A元,最少需要多少枚硬币?

假定本题至少存在一种支付方案.

0≤ci≤10^9

0≤A≤10^9

输入:

第一行有六个数字,分别代表从小到大6种面值的硬币的个数

第二行为A,代表需支付的A元

样例:

输入

3 2 1 3 0 2
620

输出

6
*/
public class 最少的硬币数 {
    static int[] coins = {1, 5, 10, 50, 100, 500};
    static int[] cnts = new int[6];
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        for (int i = 0; i < 6; i++) {
            cnts[i] = sc.nextInt();
        }
        int n = sc.nextInt();
        int res = ff(n, 5);//从面值最大的开始算
        System.out.println(res);
    }

    private static int ff(int n, int cur) {
        if (n<=0)return 0;
        if (cur==0)return n;//最小面值为1

        int value=coins[cur];//600/5
        int x=n/value;
        int num=cnts[cur];//有的数目
        int min=min(x,num);
        return min+ff(n-min*value,cur-1);
    }
}

最少的船坐最多的人

package 算法基础.贪心;

/**
 * @author 欧阳煜
 * @date 2019/8/30 9:55
 */

import java.util.Arrays;

/**
 有n个人,第i个人重量为wi。每艘船的最大载重量均为C,且最多只能乘两个人。用最少的船装载所有人。

 贪心策略:考虑最轻的人i,如果每个人都无法和他一起坐船(重量和超过C),则唯一的方案是每个人坐一艘
 否则,他应该选择能和他一起坐船的人中最重的一个j

 求需要船的数量
 */
public class 最少的船坐最多的人 {
    public static void main(String[] args) {
        int[] weight = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int n = weight.length;
        int c = 10;

        Arrays.sort(weight);
        int numOfPerson=n;
        int boats=0;
        int p1=0;
        int p2=n-1;//首尾指针
        while (numOfPerson>0){
            if (weight[p1]+weight[p2]>c){
                p2--;
                numOfPerson--;
                boats++;
            }
            else {
                p1++;
                p2--;
                numOfPerson-=2;
                boats++;
            }
        }
        System.out.println(boats);
    }
}

部分背包问题

package 算法基础.贪心;

/**
 * @author 欧阳煜
 * @date 2019/8/30 8:37
 */

import java.util.Arrays;

/**
 有n个物体,第i个物体的重量为wi,价值为vi。在总重量不超过C的情况下让总价值尽量高。

 每一个物体都可以只取走一部分,价值和重量按比例计算。

 求最大总价值

 注意:每个物体可以只拿一部分,因此一定可以让总重量恰好为C。
 */
public class 部分背包问题 {
    public static void main(String[] args) {
        int []weight={1,2,3,4,5};
        int []value={3,4,3,1,4};
        int n=5;
        double c=10;
        Goods[]goods=new Goods[n];
        for (int i = 0; i < n; i++) {
            goods[i]=new Goods(weight[i],value[i]);
        }

        Arrays.sort(goods);
        double maxValue=0;
        for (int i = n-1; i >= 0; i--) {  //从单价最大的开始拿
                if (goods[i].weight<=c){  //背包容量够
                    maxValue+=goods[i].value;
                    c-=goods[i].weight;
                }
                else {   //背包容量不够拿完
                    maxValue+=goods[i].value*(c/goods[i].weight);
                    break;
                }
        }
        System.out.println(maxValue);
    }
    public static class Goods implements Comparable<Goods>{
        int weight;
        int value;

        public Goods(int weight, int value) {
            this.weight = weight;
            this.value = value;
        }
        public double getPrice(){
            return value/(double) weight;
        }

        @Override
        public int compareTo(Goods o) {
            if (this.getPrice()==o.getPrice()) return 0;
            if (this.getPrice()<o.getPrice()) return -1;
            return 1;
        }
    }
}

动态规划

01背包问题

package 算法基础.动态规划;

import java.util.Arrays;

import static java.lang.Math.max;

/**
 * @author 欧阳煜
 * @date 2019/8/30 10:44
 */
/*
有n个重量和价值分别为wi,vi的物品,从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值。

    1≤n≤100

    1≤wi,vi≤100

    1≤W≤10000

输入:

    n=4
    (w,v)={(2,3),(1,2),(3,4),(2,2)}
    W=5

输出:

    7(选择第0,1,3号物品)

因为对每个物品只有选和不选两种情况,所以这个问题称为01背包。

 */
public class _01背包问题 {
    static int[] weight = {2, 1, 3, 2};//重量表
    static int[] value = {3, 2, 4, 2};//价值表
    static int n = 4;//物品数量
    static int w = 5;//背包的承重极限

//    static int[] weight = {1,2,3,4,5,6,7,8,9,10};//重量表
//    static int[] value = {1,5,8,16,10,17,17,20,24,30};//价值表
//    static int n = 10;//物品数量
//    static int w = 10;//背包的承重极限

    static int [][]rec=new int[n][w+1];//对于每一对n,w,保存一个结果

    public static void main(String[] args) {

        for (int i = 0; i < n; i++) {
            Arrays.fill(rec[i],-1);//因为结果可能存0
        }

        System.out.println(dfs(0,w));

        System.out.println(dp());

    }

    /**
     * 记忆性递归
     * @param i
     * @param w
     * @return
     */
    private static int dfs(int i, int w) {
        if (w<=0)return 0;//装不进去
        if (i==n)return 0;//没东西可选了

        if (rec[i][w]>=0) //1.计算之前先查询rec
            return rec[i][w];


        int ans;
        int buxuan=dfs(i+1,w);//不选择当前物品


        if (w>=weight[i]){   //一点要注意能否取等
            int xuan=value[i]+dfs(i+1,w-weight[i]);  //选择当前物品
            ans= max(xuan,buxuan);
        }else
            ans=buxuan ;//容量不够,不能选


        //2.计算之后保存到rec
        rec[i][w]=ans;
        return ans;
    }

    /**
     * 动态规划
     * @return
     */
    public static int dp(){
        int[][]dp=new int[n][w+1];
        //初始化dp表的第一行
        for (int i = 0; i < w + 1; i++) {
            if (i>=weight[0]){
                dp[0][i]=value[0];
            }
            else dp[0][i]=0;
        }
        //  其他行
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < w + 1; j++) {
                if (j>=weight[i]){  //包的容量够
                    int xuan=value[i]+dp[i-1][j-weight[i]];//选择当前物品即i号物品,剩余容量
                    //j-weight[i] 剩余容量
                    //i-1 上一行
                    //value[i]  选的物品价值
                    int buxuan=dp[i-1][j];
                    dp[i][j]= max(xuan,buxuan);
                }
                else
                    dp[i][j]=dp[i-1][j];
            }
        }
        return dp[n-1][w];//最后下角的格子
    }






}

完全背包

package 算法基础.动态规划;

import static java.lang.Math.max;

/**
 * @author 欧阳煜
 * @date 2019/8/31 10:20
 */

/**
 * 首先在初始化最后一行的时候有所不同
 *
 * 要抓的时候,先抓到这个物品的价值,然后用剩下的容量去匹配同一行
 *
 */
public class 完全背包 {
    public static void main(String[] args) {
        System.out.println(dp());
    }


    static int[] weight = {7,4,3,2};//重量表
    static int[] value = {9,5,3,1};//价值表
    static int n = 4;//物品数量
    static int w = 10;//背包的承重极限

    public static int dp(){
        int[][]dp=new int[n][w+1];
        //初始化dp表的第一行
        for (int i = 0; i < w + 1; i++)
                dp[0][i]=i/weight[0]*value[0];

        //  其他行
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < w + 1; j++) {
                if (j>=weight[i]){  //包的容量够
                    int xuan=value[i]+dp[i][j-weight[i]];//选择当前物品即i号物品,剩余容量
                    int buxuan=dp[i-1][j];
                    dp[i][j]= max(xuan,buxuan);
                }
                else
                    dp[i][j]=dp[i-1][j];
            }
        }
        return dp[n-1][w];//最后下角的格子
    }
}

数字三角形

package 算法基础.动态规划;

/**
 * @author 欧阳煜
 * @date 2019/8/31 8:32
 */



import java.util.ArrayList;
import java.util.Scanner;
import java.util.Stack;

/**
 * 数字三角形(POJ1163)<br>
 *
 * 在数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。<br>
 * 路径上的每一步都只能往左下或 右下走。只需要求出这个最大和即可,不必给出具体路径。<br>
 * 三角形的行数大于1小于等于100,数字为 0 - 99<br>
 * 输入格式:<br>
 * 5 //表示三角形的行数 接下来输入三角形<br>
 *      7
 *     3 8
 *    8 1 0
 *   2 7 4 4
 *  4 5 2 6 5
 * 要求输出最大和
 *
 *
 */
public class 数字三角形 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[][] triangle = new int[n][];
        for (int i = 0; i < n; i++) {
            triangle[i] = new int[i + 1];
            for (int j = 0; j < i + 1; j++) {
                triangle[i][j] = sc.nextInt();
            }
        }
        System.out.println(dp(triangle, 0, 0));
        System.out.println(dfs(triangle,0,0));

    }
    public static int[][]rec;
    private static int dfs( int[][] triangle, int i, int j) {

        if (i==triangle.length-1)
            return triangle[i][j];  //最后一行的话
        else
            if (rec[i][j]>0)
                return rec[i][j];
            else
                rec[i][j]=triangle[i][j]+Math.max(dfs(triangle,i+1,j),dfs(triangle,i+1,j+1));
        return rec[i][j];
    }


    private static int dfss( int[][] triangle, int i, int j) {
        if (i==triangle.length-1)
            return triangle[i][j];  //最后一行的话
        else
            return triangle[i][j]+Math.max(dfss(triangle,i+1,j),dfs(triangle,i+1,j+1));


    }



//可以用滚动数组,因为只需要依赖上一行,把上一行的数据不断覆盖就行

    private static int dp(int[][] triangle, int i, int j) {
        int row = triangle.length;
        int column = triangle[row - 1].length;//最后一行列数
        int[][]dp=new int[row][column];

        for (int k = 0; k < column; k++) {
            dp[row-1][k]=triangle[row-1][k];
        }

        for (int k = row-2; k >=0 ; k--) {
            for (int l = 0; l <=k; l++) {
                dp[k][l]=triangle[k][l]+Math.max(dp[k+1][l],dp[k+1][l+1]);
            }
        }
        return dp[0][0];

    }
}

最长递增子序列

package 算法基础.动态规划;

/**
 * @author 欧阳煜
 * @date 2019/8/31 13:00
 */

/**
 * 最长递增子序列的长度
 输入 4 2 3 1 5 6
 输出 3 (因为 2 3 5组成了最长递增子序列)
 */
public class 最长递增子序列 {
    static int[] a={4,2,3,1,5,6};
    public static void main(String[] args) {
        System.out.println(f(a));

    }

    /**
     * By Force
     * @param a
     * @return
     */
    private static int f(int[] a) {
        int maxCnt=0;
        for (int i = 0; i < a.length; i++) {  //对于每个元素都有一个cnt
            int p=i;  //当前最大元素下标
            int cnt=1;
            for (int j = i+1; j <a.length ; j++) {
                if (a[j]>a[p]){
                    cnt++;
                    p=j;
                }
            }
            maxCnt=Math.max(maxCnt,cnt);
        }
        return maxCnt;
    }



}

钢条切割

package 算法基础.动态规划;

import java.util.Arrays;

/**
 * @author 欧阳煜
 * @date 2019/8/30 16:42
 */
/*
Serling公司购买长钢条,将其切割为短钢条出售。切割工序本身没有成本支出。公司管理层希望知道最佳的切割方案。
假定我们知道Serling公司出售一段长为i英寸的钢条的价格为pi(i=1,2,…,单位为美元)。钢条的长度均为整英寸。

| 长度i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| - | - | - | - | - | - | - | - | - | - |
价格pi | 1 | 5 | 8 | 16 | 10 | 17 | 17 | 20 | 24 | 30 |

钢条切割问题是这样的:给定一段长度为n英寸的钢条和一个价格表pi(i=1,2,…n),求切割钢条方案,使得销售收益rn最大。
注意,如果长度为n英寸的钢条的价格pn足够大,最优解可能就是完全不需要切割。

 */
public class 钢条切割 {
    //跟01背包不同这个可以多次选择
    static int n=10;
    static int []p={1, 5, 8, 16, 10, 17, 17, 20, 24, 30};//p[0]表示长度为1的收益
    static int[]rec=new int[n+1];//保存下标为n的钢条最大收益

    public static void main(String[] args) {
        Arrays.fill(rec,-1);
        System.out.println(f(10));
        Arrays.fill(rec,-1);
        System.out.println(dp(10));
    }

    /**
     * dp
     * @param length
     * @return
     */
    private static int dp(int length) {
        rec[0]=0;
        for (int i = 1; i <= n; i++) {//拥有的钢条长度
            for (int j = 1; j <= i; j++) {//保留j为整段
                rec[i]=Math.max(rec[i],p[j-1]+rec[i-j]);
            }
        }
        return rec[length];
    }

    /**
     * 记忆性递归
     * @param length
     * @return
     */
    public static int dfs(int length){
        if (length==0) return 0;

        int ans=0;
        for (int i = 1; i <= length; i++) {  //尝试不同长度分割
            if (rec[length-i]==-1)
                rec[length-i]=dfs(length-i);
            int profit=p[i-1]+rec[length-i];
            ans=Math.max(ans,profit);
        }
        rec[length]=ans;
        return ans;

    }

    /**
     * normal
     * @param x
     * @return
     */
    public static int f(int x){
        if (x==0) return 0;

        int ans=0;
        for (int i = 1; i <= x; i++) {  //尝试不同长度分割
            int profit=p[i-1]+f(x-i);
            ans=Math.max(ans,profit);
        }
        return ans;

    }
}

  • 14
    点赞
  • 102
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值