算法基础–模板及经典题型

  • Post author:
  • Post category:其他





模板持续更新中… …

1 、判断闰年

闰年:能被400整除;或 能被4整除,但不能被100整除。

static boolean is (int x ){
    return x%400==0 || (x%4==0 && x%100!=0)
}

2 、 最小公倍数

//least Common Multiple—> lcm

// 两个数比较
static int lcm1(int a,int b){
    return a*b/gcd(a,b);
}
static int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}

//多个数的最小公倍数
static int lcm2(int [] a){
    int n = a.length;
    int L = a[0];
    for(int i=1;i<n;i++)
        L = lcm1(L,a[i]);
    return L;
}


3.1 、最大公约数 — 辗转相除法(欧几里得算法)

//Greatest Common Divisor—–> gcd

此算法当 a、b两个整数较大时,做a%b取模运算的性能会比较差。

//求两个数的最大公约数
static int gcd1 (int a,int b){
    return b==0?a:gcd1(b,a%b);
}

//求多个数的最大公约数
static int gcd2(int [] a){
    int n = a.length;
    int L = a[0];
    for(int i=1;i<n;i++)
        L = gcd1(L,a[i]);
    return L;
} 

3.2 、最大公约数 — 更相减损术

此方法避免了大整数取模可能出现的性能问题,但当两个数相差较大时(如:2,999990),其运算次数远大于辗转相除法。so,看3.3

static int gcd(int a,int b){
    if(a==b)
        return a;
    int big = a>b ? a : b;
    int small = a<b ? a : b;
    return gcd(big -small , samll);
}

   

3.3 、最大公约数 — 更相减损术+移位运算

此模板代码中,判断整数奇偶性的方式是让整数和1进行与运算,如果(a&1)==0,则说明整数a是偶数;如果(a&1)!=0,则说明整数a是奇数。

此算法的基本思路:

#当a和b均为偶数时:

gcd(a,b) = 2 x gcd(a/2 , b/2) = 2 x gcd(a>>1 , b>>1)

#当a为偶数,b为奇数时,gcd(a,b) = gcd(a/2 , b) = gcd(a>>1,b)

#当a为奇数,b为偶数时,gcd(a,b) = gcd(a,b/2)=gcd(a,b>>1)

#当a和b均为奇数时,先利用更相减损术运算一次,gcd(a,b) = gcd(b,a-b),此时 a-b必然是偶数,然后又可以继续进行移位运算。

//GetCommonDivisor
static int gcd(int a,int b){
    if(a==b)
        return a;
    if((a&1)==0 && (n&1)==0){
        return gcd(a>>1 , b>>1)<<1 ; 
    }else if((a&1)==0 && (b&1)!=0){
        return gcd(a>>1,b);
    }else if((a&1)!=0 && (b&1)==0){
        return gcd(a,b>>1);
    }else{
        int big = a>b ? a : b;
        int small = a<b ? a : b;
        return gcd(big -small , samll);
    }
}

3.4 、 欧几里得算法扩展–裴蜀(贝祖)等式

对任何整数 a,b 和他们的最大公约数d,关于未知数 x和 y的线性丢番图方程(称为裴蜀等式):

ax + by = m 有整数解时,当且仅当m是d的倍数

裴蜀等式 有解时必然有无穷多个整数解,每组解x,y都称为裴蜀数,可用扩展欧几里得算法求得

方程 12x + 42y = 6 有解

特别的, 方程ax+ by =1 有整数解当且仅当 a和b 互素

/**
*  扩展欧几里得
* 调用完成后 x、y是ax + by=gcd(a,b)的解*/
static long x = 0,y = 0;
public static ext_gcd(long a , long b){
    if(b==0){
        x = 1;
        y = 0;
        return a;
    }
    long res = ext_gcd(b,a%b);
    long x1 = x; // 备份 x
    x = y ;  //  更新 x
    y = x1 - a/b*y ;  //  更新 y
    return res;
}

/**
*  线性方程
*  ax + by = m  当 m是gcd(a,b)倍数时有解
* 等价于 ax = m mod b     */
public static long linearEquation(long a , long b, long m)throws Exception{
    long d = ext_gcd(a,b);
    // m不是gcd(a,b)的倍数时
    if(m % d != 0) throw new Exception("无解")long n = m / d;   //约一下,考虑m是d的倍数
    x *= n;
    y *= n;
    return d;
}

4.1 、筛素数O(n^(3/2))

static boolean is(int n){
    if(n==1)
        return false;
    for(int i=2;i<Math.sqrt(n);i++)
        if(n%i==0)
            return false;
    return true;
}

4.2 、 埃氏筛法-素数

利用数组下标,再依次修改数组是否为素数。

素数定理:1-n的数中的素数个数为: n/logn,用于开数组的大小。

测试得到:该方法求第十万个素数 用时 156ms .

public static long CountPN(int N ){
    //N是 第N个素数
    //已知在整数x内大概有 x/log(x)个素数
    // 以下几行代码 仅为开辟数组空间所用,如果空间足够,直接开Long_MaxValue也可。
    int n = 2while(n/Math.log(n) < N){
        n++;
    }
    // 开辟一个数组,下标是自然数,值是标记
    //基本思路是筛选法,把非素数标记出来
    boolean arr[] = new boolean [n];
    int x = 2;
    while(x<n){
        if(arr[x] == true){//true表示不是素数
            x++;
            continue;
        }
        // 对每个x,我们从2倍开始,对x的k倍全部标记为-1
        int k = 2;
        while(x*k < n){
            arr[x * k] = true;
            k++;
        }
        x++;
    }
    // 筛完之后,这个很长的数组里面非素数下标对应的值为 true
    int count = 0;
    long result = 0;
    for(int i=2;i<arr.length;i++){
        //是素数 计数+1
        if(arr[i] == false){
            count++;
        }
        if(sum == N)
            result = i;
    }
    return result;
}

5 、 进制问题

手工取余法

※ Integer.toString( i , radix ) ; 将i转为radix进制

5.1 、进制(位运算 )问题 — 判断一个整数是否为

2

的整数次幂

如果采用循环,2^n>num时 执行return ,必超时。

如果将左移位代替 for循环中的 乘2 运算,虽会提高性能,但时间复杂度仍为O(logn) , O(1)解法 看下面代码

此方法运用了:2的整数次幂的二进制表示为:第一位为1,其余全为0;2的整数次幂-1的二进制表示:全为1

例 64 —-1000000 | … 63 —-111111

。。 32 —-100000 | … 31—-11111

public static boolean isPowerOf(int num){
    return (num & num-1) == 0;
 }

5.2 、 进制(位运算 )问题 —- 二进制中1的个数

输入一个整数,输出该数二进制表示中n的个数

n和n-1做一次 与运算,消除一个最低位的1,一次类推

static int count1(int n){
    int count = 0;
    while( n!= 0){
        n = (n-1)&n;
        count++;
    }
    return count;
}

5.3 、 进制(位运算 )问题 —- 将整数的奇偶位互换

整数的二进制位互换

static int m(int n){
    int ou = n & 0xaaaaaaaa ; // 和 1010 1010 。。。 做与运算,取出偶数位
    int ji = n & 0x55555555 ; // 和 0101 0101 。。。 做与运算,取出奇数为
    return (ou>>1) ^ (ji<<1)// 异或运算  连起来
}

5.4 、 进制(位运算 )问题 —- 0~1间浮点实数的二进制表示

例:给定一个介于0和1之间的实数,如0.625,类型为double,打印它的二进制表示:0.101

因为小数点后的二进制分别表示为0.5,0.25,0.125…

如果该数字无法精确地用32位以内的二进制表示,则打印“ERROR”

整数的10进制转二进制是除以2,小数的是乘2(每次乘2,判断整数位,整数位是1则二进制写1,不足1写0,扣掉整数位,重复)

static String binaryX(int m){
    StringBuilder sb = new StringBuilder("0.");
    while(m > 0){
        //乘2
        double r = m*2;
        if(r>=1){
            sb.append("1");
            //消除整数部分
            m = r-1;
        }else{
            sb.append("0");
            m = r;
        }
        if(sb.length()>34){ // 32位二进制,+ 0. 的两位
            return "ERROR";
        }
    }
    return sb.toString();
}

5.5 、 进制(位运算 )问题 —- 出现 k 次与出现 1 次

数组中只有一个数出现了1次,其他数都出现了k次,请输出只出现1次的数。

出现k次的数 做不进位的k进制相加为0

//个人忽然感觉有比k进制求解更优的方法,所以那个k进制的破方法就不写了!
//以下为个人思路,题目太简单 其实没有作为模板的意义,可以忽略本题
//时间复杂度 O(n)  空间复杂度O(n)
static int search1(int [] a){
    Array.sort(a);
    if(a[0]!=a[1] && a[1]==a[2])
        return a[0];
    if(a[a.length-1] != a[a.length-2] && a[a.length-2] ==a[a.length-3])
        return a[a.length-1];
    else{
        for(int i=1 ;i<a.length-2;i++){
            if(a[i]!=a[i-1] && a[i]!=a[i+1])
                return a[i];
        }
    }
    return -1}

5.6 、 进制(位运算 )问题 —- Nim游戏(此题思维很重要)

一共有N堆石子,编号 1…n ,第i堆中有a[i]个石子

每一次操作Alice和Bob可以从任意一堆石子中取出任意数量的石子,至少取一颗,至多取出这一堆剩下的所有石子。

两人轮流行动,取光所有石子的雅芳获胜。Alice先手

.

给定a,假设两人都采用最优策略,谁会获胜?

// ※ 所有堆的石子数,面临全部异或为0 则输
static boolean solve(int [] a){
    int res = 0;
    for(int i=0;i<a.length;i++){
        res ^= a[i];
    }
    return res != 0 ;
}

5.7 、 进制(位运算 )问题 —- 阶梯尼姆博弈

5.8 、进制(位运算 )问题 —- 天平称重(蓝桥杯真题)

天平称重:变种三进制

用天平称重时,我们希望用尽可能少的砝码组合称出尽可能多的重量。如果有无限个砝码,但他们的重量分别是 1,3 , 9, 27 ,… …等3的指数幂。神奇之处在于用它们的组合可以称出任意整数重量(砝码允许放在左右两个盘中)。

.

本题目要求编程实现:对用户给定的重量,给出砝码组合方案,重量<1000000

例如:

用户输入

5

程序输出

9-3-1

import java.util.Scanner;
import java.util.List;
public class Main{
    public static void main(String [] args){
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        //转成3进制
        String x = Integer.toString(n,3);
        //翻转后,转成字符数组
        char[]arr = new StringBuilder(x).reverse().toString().toCharArray();
        //放处理之后的 0  -1   1
        List<Integer> list = new ArrayList<>();
        for(int i=0;i<arr.length;i++){
            if(arr[i] == '2'){
                list.add(0,-1);// -1插在开头
                if(i==arr.length-1){
                    list.add(0,1); // 最后一个字符,进位
                }else{
                    ++arr[i+1]; // 否则,对下一个数字加1
                }
            }else if(arr[i] == '3'){
                list.add(0,0);   //插入0
                //更高位进1
                if(i==arr.length-1){
                    list.add(0,1);
                }else{
                    ++arr[i+1];
                }
            }else{
                list.add(0,arr[i]-'0');
            }
        }
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<list.size();i++){
            if(list.get(i)==1)
                sb.append("+").append((int)Math.pow(3,list.size()-i-1));
            if(list.get(i)==-1)
                sb.append("-").append((int)Math.pow(3,list.size()-i-1));

        }
        System.out.Println(sb.subString(1));
    }
}

6.1、 子数组最大累加和

给定一个数组arr,返回子数组的最大累加和

例:arr = [1,-2,3,5,-2,6,-1] ; 所有的子数组中[3,5,-2,6]可以累加出

最大的和12,所以返回12

public class Main{
	//法一:暴力破解 O(n^2)
    static void findByForce(int [] arr){
        int maxSum = arr[0];
        for(int j=0;j<arr.length;j++){
            int sum = arr[j] ;//某个元素为子数组的第一个元素
            int maxOfJ = sum;
            for(int i=j+1;i<arr.length;i++){
                sum+=arr[i];//累加后续元素
                if(sum>maxOfJ){
                    maxOfJ = sum;
                }
            }
            if(maxOfJ>maxSum){
                maxSum = maxOfJ;
            }
        }
        System.out.println(maxSum);
    }
    
    	//  法二: 递推法 O (n)
    	//※ 单向扫描求和,sum是负数则丢弃,是正数保留
    static int findByDp(int [] arr){
        int sunJ = arr[0]; //前J个元素的最大贡献
        int max = sumJ;
        for(int j=1;j<arr.length;j++){
            if(sumJ => 0){  //左子表的最大和为正,继续向后加
                sumJ += arr[j];
            }else{
                sumJ = arr[j];
            }
            if(sumJ > max){
                max = sumJ;
            }
        }
        return max;
    }
    public static void main(String [] args){
        int [] arr = {100,-20,55,-79,20,22,73,-35};
        findByForce(arr);
        System.out.println(findByDp(arr));
    }
}

6.2 、 子矩阵最大累加和

给定一个矩阵matrix , 其中的值有正,有负,有0 ,返回子矩阵的最大累加和。

例如,matrix为:

-1 -1 -1

-1 .2 .2

-1 -1 -1

其中最大累加和的子矩阵为: 2 2

所以返回 4

//本题暴力解法不可取,时间复杂度太高
//先按列求和(单行按列--到--所有行按列求和),化为一维数组
//再运用上一题中的递推法,最终比较出最大子矩阵的累加和
import java.util.Scanner;
public class Main{  
	//  O(n^3)
    private static int maxSum(int [][] matrix){
        int beginRow = 0 ; //以它为起始行
        int M = matrix.length;
        int N = matrix[0].length;
        int [] sums = new int [N]; //按列求和
        int max = 0;    //历史最大的子矩阵和
        while(beginRow < M){  //起始行
            for(int i = beginRow ;i<M;i++){  //从起始行到第i行
                //按列累加
                for(int j=0;j<N;j++){
                    sums[j]+=matrix[i][j];
                }
                //累加完成
                int t = finByDp(sums); //用到上一题的递推法
                if(t > max)  max = t;
            }
            Arrays.fill(sums,0);//快速地将sums的每个元素都设为0
            //另起一行作为起始行,把sum清零
            beginRow++;
        }
        return max;
    }
    public static void main(String [] args){
        Scanner in = new Scanner(System.in);
        int m = in.nextInt();
        int n = in.nextInt();
        int [][] a = new int [m][n];
        //输入数据代码 略
        //竞赛中 尽量考虑边界情况,及矩阵为空,m=n=0,
        //执行方法体时,则会出现下标0 越界的情况,也可将此判断语句放在方法体内
        if(a.length==0){
            System.out.println("0")
        }else{
            System.out.println(maxSum(a));
        }
    }
}

7 .、 字符串操作

字符串

7.1 、 字符串操作—字符串压缩

字符串压缩问题:将给定的字符串,按照规格压缩,输出压缩后的字符串。压缩规格为:相同字符连续,则压缩为“字符+数字个数”,如”aaaa”压缩为”a4”,srcStr = “aaacccddef” 返回:“a3c3d2ef”

import java.util.Scanner;
public class Main{
     public static StringBuffer yasuo(String s) {
	    int lins = 0 ,n = s.length();
	    StringBuffer str = new StringBuffer();
	    for(int i=0;i<n;i+=lins) {
	        int count = 0;
	        for(int j=i;j<n;j++) {
	            if(s.charAt(i)==s.charAt(j))  
	                count++ ;
	            else                   
	                break ;
		 }
		 if(count==1)  
		     str.append(s.charAt(i));
		 else            
		     str.append(s.charAt(i)).append(count);
	         lins = count;   
	    } 
	    return str;
	}  
    public static void main(String[] args) {
	      Scanner in = new Scanner(System.in);
	      String s1 = in.nextLine();
	      System.out.println( yasuo(s1) );   
    }
}

7.2 、 字符串操作— 去除字符串中连续出现的K个字符char

移除字符串中连续出现的k个字符 ch

可以用扫描字符数组的方法,但是用正则表达式更为快捷

    static String remove(String str , int k ,char ch){
        String regexp = ch+"{"+K+"}";
        return str.replaceAll(regexp,"");
    }

7.3 、 字符串操作—判断回文字符串

“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。

// 方法一 : 字符逐一比较
static boolean isHuiWen(String str){
    int count = 0;
    for(int i = 0; i < str.length()/2; i++) {
        if((str.substring(i,i+1)).equals(str.substring(str.length() -1-i,str.length()-i))) {
            count ++;
        }
    }
    if(count == str.length()/2) {
        return true;
    }
    return false;
} 

// 方法二 : 使用StringBuffer/StringBuild 的reverse()方法进行字符串翻转
static boolean isHuiWen(String str){
    StringBuilder sb=new StringBuilder(str);  
    sb.reverse();
    String newStr=new String(sb);  
    if(str.equals(newStr)){  
        return true;
    }
    return false;
}

7.4 、 字符串操作—字符串匹配–RabinKarp(上)

O(m*n)

RabinKarp:hash法

  • 对目标字符串按d进制求值,mod h 取余作为其 hash
  • 对源串,依次求出m个字符的hash ,保存在数组中(滚动计算)
  • 匹配时,只需比对目标串的hash值和预存的源串的hash值表

需要匹配的字符串为三个字符时,计算哈希值示例:

( ( 0+A1 ) *31 ) + A2 ) * 31 + A3 = A1 * 31^2 +A2 * 31 + A3

更多字符时同理递推。计算哈希值的方法有很多种,尽量选择不产生重复值的方法

    //  p为模式串 , s为源串
static void match(String p , String s){
    long hash_p = hash(p);  // p的hash值
    int p_len = p.length();
    for(int i=0;i+p_len <= s.length();i++){
        long hash_i = hash(s.subString(i,i+p_len));
        if(hash_i == hash_p){
            if(p == s.substring(i,i+n))//此行代码为了避免hash冲突
                System.out.println("match:"+i);
        }
    }
}
/**
* 使用 100000 个不同字符串产生的冲突数,大概在0~3波动,
* 使用100百万个不同的字符串,冲突数大概110+范围波动
*/
static long seed = 31 ; //求哈希值的一个种子,素数
static long hash(String str){
    long hash = 0;
    for(int i=0; i != str.length() ; i++){
        hash = seed * hash + str.charAt(i);
    }
    return hash % Long.MAX_VALUE;
}

7.5 、 字符串操作—字符串匹配–RabinKarp(下)

O(2m+n)

RabinKarp:滚动hash法

-计算了前1-3字符的hash 值value1,在计算2-4时的值value2时: value2 = (value1+A4)* 31 – A1* 31^4

此滚动方法降低了时间复杂度

static void match(String p,String s){
    long hash_p = hash(p); // p的hash值
    long[] hashOfS = hash(s,p.length());
    match(hash_p,hashOfS);
}
static void match(long hash_p , long []hash_s){
    for(int i=0;i<hash_s.length;i++){
        if(hash_s[i] == hash_p ){
            if(p == s.substring(i,i+n))//此行代码为了避免hash冲突
                System.out.Println("math:"+i)
        }
    }
}
/**
* n 是子串(模式串)的长度
* 用滚定方法求出s中长度为n的每个子串的hash,组成一个hash数组
*/
static long seed = 31;
static long hash(String str){
    long hash = 0;
    for(int i=0; i != str.length() ; i++){
        hash = seed * hash + str.charAt(i);
    }
    return hash % Long.MAX_VALUE;
}
static long[] hash(String s,int n){
    long[]res = new long[s.length()-n+1];
    //前m个字符的hash
    res[0] = hash(s.substring(0,n));
    for(int i = n;i<s.length();i++){
        char newChar = s.charAt(i);
        char ochar = s.charAt(i-n);
        //前n个字符的hash*seed-前n个字符的第一字符*seed的n次方
        long v = (res[i-n]*seed + newChar - (long)Math.pow(seed,n)*ochar) % Long.MAX_VALUE;
        if(v<0) v+=Long.MAX_VALUE;
        res[i-n+1] = v;
    }
    return res;
}

7.6 、 字符串操作—字符串匹配–KMP(上)-暴力破解

//如果匹配成功返回 源字符串中对应模式串的第一个字符索引
static int indexOf(String s,Stirng p){
    int i = 0;
    int sc = i;
    int j = 0;
    while(sc < s.length()){
        if(s.charAt(sc) == p.charAt(j)){
            sc++;
            j++;
            if(j==p.length())
                return i;
        }else{
            i++;
            sc = i; //扫描指针以i为起点
            j = 0; // j 恢复为0
        }
    }
    return -1}

7.6 、 字符串操作—字符串匹配–KMP(下)- next数组

next[ j ] = k —–> j失配,j回溯到K

public class KMP {
    private int[][] dp;
    private String pat;
    
    public KMP(String pat) {
        this.pat = pat;
        int M = pat.length();
        // dp[状态][字符] = 下个状态
        dp = new int[M][256];
        // base case
        dp[0][pat.charAt(0)] = 1;
        // 影子状态 X 初始为 0
        int X = 0;
        // 构建状态转移图(稍改的更紧凑了)
        for (int j = 1; j < M; j++) {
            for (int c = 0; c < 256; c++) {
                dp[j][c] = dp[X][c];
            dp[j][pat.charAt(j)] = j + 1;
            // 更新影子状态
            X = dp[X][pat.charAt(j)];
        }
    }
    public int search(String txt) {
        int M = pat.length();
        int N = txt.length();
        // pat 的初始态为 0
        int j = 0;
        for (int i = 0; i < N; i++) {
            // 计算 pat 的下一个状态
            j = dp[j][txt.charAt(i)];
            // 到达终止态,返回结果
            if (j == M) return i - M + 1;
        }
        // 没到达终止态,匹配失败
        return -1;
    }
}

7.7 、 字符串操作—字符串匹配–后缀数组

7.8 、 字符串操作—字符串匹配–前缀树(字典树)

8.1 、 快速幂运算– a的n次方(上)

以最快的速度求a的n次方

O(logn)

例:
在这里插入图片描述

public static int ex(int a ,int n){
    if(n==1)
        return a;
    int temp = a; //a的一次方
    int res = 1;
    int exponent = 1;
    while((exponent << 1) <n){
        temp = temp * temp ;
        exponent = exponent<<1;
    }
    res *= ex(a, n-exponent);
    return res * temp;
}

8.2 、快速幂运算 – n的m次方(下)巧算

public static long ex2(long n , long m){
    long pingfangshu = n ; //n的1次方
    long result = 1 ;
    while( m != 0){
        // 遇1累乘现在的幂
        if((m&1)==1)
            result *= pingfangshu;
        //每移一位,幂累乘方一次
        pingfangshu *= pingfangshu ;
        // 右移一位
        m >>= 1;
    }
    return result;
}

8.3 、 快速幂运算 – 矩阵幂运算

因为矩阵快速幂运算会用到矩阵相乘,所以放在了9.3

9.1 、 矩阵相乘–方阵

static int[][] multiply(int[][] a,int[][] b){
    int n = a.length;
    int[][] ans = new int[n][n];  
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            for(int k=0;k<n;k++)
                ans[i][j] += a[i][k]*a[k][j];
     return ans;
 }

9.2 、 矩阵相乘–一般矩阵

static long[][] multiply(long [][] m1,long [][] m2){
    int m1Row = m1.length;
    int m1Line = m1[0].length;
    if(m1Line != m2.length) throw new IllArgumentException();
    int m2Line = m2[0].length;
    //新矩阵的行数为m1的行,列数为m2的列数
    long[][] result = new long[m1.row][m2.Line];
    for(int i=0;i<m1Row;i++){
        for(j=0;j<m2Line;j++){
            for(int k=0;k<m1Line;k++){
                result[i][j] += m1[i][k]*m2[k][j]
            }
        }
    }
    return result;
}

9.3 矩阵相乘 – 矩阵快速幂运算

//求矩阵m的p次方
public static long[][] matrixPower(long[][] matrix , long p){
    //初始化结果为单位矩阵,对角线为1
    long[][] result = new long[matrix.length][matrix[0].length];
    //单位矩阵相当于整数1
    for(int i =0;i<result.length;i++){
        result[i][i] = 1;
    }
    // 平方数
    long[][] pingfang = matrix; // 一次方
    while(p !=0){
        if((p & 1) !=0){//当前二进制位的最低位为1 ,将当前平方数乘到结果中
            result = multiply(result,pingfang);
        }
        //平方数继续上翻
        pingfang = multiply(pingfnag,pingfang);
        p>>=1;
    }
    return result;
}

10.1 、 01背包

f[v]=max{f[v],f[v-c[i]]+w[j]}此状态转移方程,将原来的二维数组变为一维数组。降低了空间复杂的,之所以一维数组也能得到结果,是因为通过一维数组的循环存入数据得到的。对二维数组而言,调用数组左上方的数据。而一维数组通过双重循环,外层正序,内层逆序的原则,使要用到的数据 已经存储在一维数组 当前索引的左侧。

import java.util.Scanner;
public class 01backpack{
     public static void main(String [] args){
         Scanner in = new Scanner(System.in);
         int m = in.nextInt();//大小
         int n = in.nextInt();//物品数
         int [] dp = new int [m+1];//开背包质量的大小
         for(int i=1;i<=n;i++){
             int v = in.nextInt(); //物品体积
             int w = in.nextInt(); //物品价值
             for(int j=m;j>=v;j--)
                 dp[j] = Math.max(dp[j],dp[j-v]+w);
         }
         System.out.println(dp[m]);
     }
}

10.2 、完全背包

import java.util.Scanner;
public class FullBackpack{    
     public static void main(String [] args){
         Scanner in = new Scanner(System.in);
         int m = in.nextInt();//大小
         int n = in.nextInt();//物品数
         int [] dp = new int [m+1];//开背包质量的大小
 	 for(int i=1;i<=n;i++){
             int v = in.nextInt(); //物品体积
             int w = in.nextInt(); //物品价值
             for(int j=v;j<=m;j++)
                 dp[j] = Math.max(dp[j],dp[j-v]+w);
         }
         System.out.println(dp[m]);
     }
 }



版权声明:本文为weixin_44107920原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。