蓝桥学习笔记

import java.util.ArrayList;

import java.util.Scanner;

public class demo19 {

/*        0 1 背包问题

        有 N件物品和一个容量是 V 的背包。每件物品只能使用一次。

        第 i件物品的体积是 vi,价值是 wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

        输出最大价值。

        4 5

        1 2

        2 4

        3 4

        4 5

        * */

    static int n,V;

    public static void main(String[] args) {

        Scanner sc=new Scanner(System.in);

        n= sc.nextInt();

        V=sc.nextInt();

        int []v=new int[n+1];//从1~n

        int []w=new int[n+1];

        int []s=new int[n+1];

        for (int i = 1; i <=n; i++) {

            v[i]=sc.nextInt();

            w[i]=sc.nextInt();

            s[i]=sc.nextInt();

        }

        //zeroOne(n,V,v,w);

        //Entire(n,V,v,w);

       // Multiple(n,V,v,w,s);

        MultipleImprove(n,V,v,w,s);

    }

    static  void zeroOne(int n,int V,int v[],int w[]){

        int dp[][]=new int[n+1][V+1];//dp[i][j]表示从前i个元素,背包体积为j时选取的价值最大值

        for (int i = 1; i <=n ; i++) {

            for (int j = 1; j <=V ; j++) {

                if(v[i]>j)dp[i][j]=dp[i-1][j];

                else dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);

            }

        }

        System.out.println(dp[n][V]);

    }

    //完全背包:每种物品的个数无限可用

    static  void Entire(int n,int V,int v[],int w[]){

     int dp[][]=new int[n+1][V+1];//这里是为了防止数组索引越界

        for (int i = 1; i <=n ; i++) {

            for (int j = 1; j <=V ; j++) {

                if(v[i]>j)dp[i][j]=dp[i-1][j];

/*                else for (int k = 0; k*v[i]<=j ; k++) {

                    dp[i][j]=Math.max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);

                }*/

                //经过数学公式推导进行化简可以简化得到以下式子

                dp[i][j]=Math.max(dp[i-1][j],dp[i][j-v[i]]+w[i]);

            }

        }

        System.out.println(dp[n][V]);

    }

    //多重背包1 每种物品的数目有限 定义一个数组s[i]来存储物品的数目

    static void Multiple(int n,int V,int v[],int w[],int s[]){

        int dp[][]=new int[n+1][V+1];

        //i表示现在取到了第i种物品

        for (int i = 1; i <=n ; i++) {

            //j表示现在背包的容量

            for (int j = 1; j <=V ; j++) {

                if(v[i]>j)dp[i][j]=dp[i-1][j];

                    //k表示现在取的第i种物品的数目

                else for (int k = 0; k*v[i]<=j&&k<=s[i] ; k++) {

                    dp[i][j]=Math.max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);

                }

            }

        }

        System.out.println(dp[n][V]);

    }

    /*

    多重背包 的优化 使用二进制优化

    我们知道一个十进制数字可以用二进制数来表示,这里我们将物品的数目拆分为二进制的

    例如 8可以由2^0+2^1+2^2+1

    因为物品数目可数,所以可以将其转化为01背包问题

    我们只需要对原来数组进行拆分即可 列如:

     物品1      体积2     价值3     数量7   7=1+2+4可以变化为 14=1+2+4+7

 1   物品1.1    体积2     价值3     数量1

 2   物品1.2    体积2*2   价值3*2   数量1

 4   物品1.3    体积2*4   价值3*4   数量1

 注意 若物品数量为8 则8=1+2+4+1;

    原来的7次比较就变为了3次比较

    * */

    static void MultipleImprove(int n,int V,int v[],int w[],int s[]){

        //现在对物品进行拆分,因为不知道拆分完后共有多少个物品,这里选择利用list集合,需要定义一个类

        ArrayList<goods> list=new ArrayList<>();

        list.add(new goods(0,0));//将第一个元素设为0,防止后续操作index溢出

        for (int i = 1; i <=n ; i++) {

            for (int k = 1;k<s[i]; k*=2) {

                list.add(new goods(k*v[i],k*w[i]));

                s[i]-=k;

            }

            if(s[i]>0)list.add(new goods(s[i]*v[i],s[i]*w[i]));

        }

        //接下来就是01背包问题

        int l=list.size();

        int dp[][]=new int[l][V+1];

        //i表示现在取到了第i种物品

        for (int i = 1; i <=l-1 ; i++) {

            for (int j = 1; j <=V ; j++) {

                if(list.get(i).getV()>j)dp[i][j]=dp[i-1][j];

                else dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-list.get(i).getV()]+list.get(i).getW());

            }

        }

        System.out.println(dp[l-1][V]);

    }

    static class goods{

        private int v;

        private int w;

        public goods(int v, int w) {

            this.v = v;

            this.w = w;

        }

     public int getV(){

            return this.v;

     }

     public int getW(){

            return this.w;

      }

    }

}

import java.util.Scanner;

public class demo20 {

    /*

    * 数位dp

    * 小蓝最近在学习二进制。他想知道 1到 N 中有多少个数满足其二进制表示中恰好有 K 个 1。你能帮助他吗?

    对于所有评测用例,1≤N≤10^18,1≤K≤50

    * 思路:1.判断该题目是数位dp

    *      2.设计状体

    *      3.套用模板 dfs(int pos,(state),boolean lead,boolean limit)pos:当前位置 state:状态自己寻找 lead:前导0影响 limit:是否限制

    *  */

    //2^60=1,152,921,504,606,846,976 19位 一般取值要大一点 避免发生未知的错误

    static long dp[][]=new long[65][65];//dp[pos][state] 表示在pos位满足状态的数目

    static long n;

    static int k;

    static int num[]=new int[65];

    public static void main(String[] args) {

        Scanner sc=new Scanner(System.in);

        n=sc.nextLong();

        k= sc.nextInt();

        System.out.println(solve(n, k));

    }

    static long dfs(int pos,int sum,boolean lead,boolean limit){

        if(pos==0)return sum==k?1:0;//边界条件

        if(dp[pos][sum]!=-1&&!limit&&!lead)return dp[pos][sum];//如果没有限制并且没有前导0返回前面已经存储过的值

        int up=limit?num[pos] :1;// 寻找上限值

        long res=0;

        for (int i = 0; i <=up ; i++) {

            //有时候有前导0需要剪枝处理

            res += dfs(pos-1, sum + (i == 1 ? 1 : 0), lead && i == 0, limit &&i == num[pos]);

        }

        if(!limit&&!lead) dp[pos][sum]=res;//如果没有限制并且没有前导0存储当前结果

        return res;

    }

    static long solve(long n,int k){

        //先将dp[i][sum]初始化为-1;

        for (int i = 0; i < dp.length ; i++) {

            for (int j = 0; j < dp.length; j++) {

                dp[i][j]=-1;

            }

        }

        //将n转化为二进制

        int pos=0;

        while (n>0){

            num[++pos]=(int)(n%2);

            n/=2;

        }

        return dfs(pos,0,true,true);

    }

}

import java.util.Scanner;

public class demo25 {

    //manacher算法

    public static void main(String[] args) {

        Scanner sc=new Scanner(System.in);

        String s= sc.next();

        manacher(s);

    }

    /*

    * '$'是为了设置俩个哨兵防止数组索引越界

    * '#'在原来的数组中前一个和最后一个以及元素之间插入'#'让数组长度变为奇数个。

    * l,r是盒子的区间

    * d[i]是该位置最大的匹配长度

    * 1.如果i在盒子里面i<r那么d[i]=min(d[l+r-i],r-i)

    * 2.如果i不在盒子里面则暴力求解while(arr[i-d[i]]==arr[i+d[i]])d[i]++;

    * 3.如果l+d[i]>=r更新盒子区间l=i-d[i]+1,r=i+d[i]-1,

    * */

    static void manacher(String s){

        int ll=s.length();

        int n=2*ll+1+2;

        char []arr=new char[n];

        int  []d=new int[n];

        d[1]=1;

        int pos=0;

        arr[pos++]='$';

        arr[pos++]='#';

        for (int i = 0; i < ll; i++) {

            arr[pos++]=s.charAt(i);

            arr[pos++]='#';

        }

        arr[pos]='$';

        for (int i = 2,l=0,r=1; i <=n-2 ; i++) {

            if(i<=r)d[i]=Math.min(d[l+r-i],r-i+1);

            while(arr[i-d[i]]==arr[i+d[i]])d[i]++;

            if(d[i]>r){l=i-d[i]+1;r=i+d[i]-1;}

        }

        for (int i = 1; i <=n-2; i++) {

            if(d[n-1]<d[i])d[n-1]=d[i];

        }

        System.out.println(d[n-1]-1);

    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值