未见过类型题每周总结(个人向)

1.SY6 合唱团

题目

 解析

因为有三个限定条件,只设dp[i]表示前i个数的乘积得不到方程,所以设d[i][j]表示在前i名学生中选出j名学生,其中第j名学生必选所获得的乘积最大值又因为数据中可能存在负值,所以在计算负值的乘积时要找到前面的最小值,所以设p[i][j]表示在i名学生中选出j名学生

其中第j名学生必选所获得的乘积最小值d[i][j]=max(arr[i]*Math.max(d[prev][j-1],p[prev][j-1]))

p[i][j]=min(arr[i]*Math.min(d[prev][j-1],p[prev][j-1]))其中,

prev>=max(i-d,j-1)&&prev<=i-1&&prev>=0初始化把d[i][1],p[i][1]列全初始化为arr[i],因为只取一个时自身是必取的。最后返回max(d[k][k]~d[n][k])。

代码 

import java.util.*;
import java.io.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) throws IOException {//SY6 合唱团
        //因为读取数据量大,所以使用快读
        Read in=new Read();
        int n=in.nextInt();
        //读入n个数据
        int[] arr=new int[n+1];
        for(int i=1;i<=n;i++) {
            arr[i]=in.nextInt();
        }
        int k=in.nextInt();//k名学生    
        int d=in.nextInt();//相差值不超过d
        //设d[i][j]表示在前i名学生中选出j名学生,其中第j名学生必选
        //所获得的乘积最大值
        //又因为数据中可能存在负值,所以在计算负值的乘积时要找到前面的最小值,所以
        //设p[i][j]表示在i名学生中选出j名学生,其中第j名学生必选所获得的乘积最小值
        //d[i][j]=max(arr[i]*Math.max(d[prev][j-1],p[prev][j-1]))
        //p[i][j]=min(arr[i]*Math.min(d[prev][j-1],p[prev][j-1]))
        //其中,prev>=max(i-d,j-1)&&prev<=i-1&&prev>=0
        //初始化把d[i][1],p[i][1]列全初始化为arr[i],因为取一个自身是必取的
        //最后返回max(d[k][k]~d[n][k])
        long[][] dd=new long[n+1][k+1];
        long[][] p=new long[n+1][k+1];
        for(int i=1;i<=n;i++) {
            dd[i][1]=arr[i];
            p[i][1]=arr[i];
            for(int j=2;j<=k&&j<=i;j++) {
                dd[i][j]=Long.MIN_VALUE;
                p[i][j]=Long.MAX_VALUE;
                for(int prev=i-1;prev>=Math.max(j-1,i-d);prev--) {
                    dd[i][j]=Math.max(Math.max(arr[i]*dd[prev][j-1],arr[i]*p[prev][j-1]),dd[i][j]);
                    p[i][j]=Math.min(Math.min(arr[i]*dd[prev][j-1],arr[i]*p[prev][j-1]),p[i][j]);
                }
            }
        }
        long max=Long.MIN_VALUE;
        for(int i= k;i<=n;i++) {
            max=Math.max(max,dd[i][k]);
        }
        System.out.print(max);
    }
}
class Read {
    StringTokenizer st=new StringTokenizer("");
    BufferedReader read=new BufferedReader(new InputStreamReader(System.in));
    String next() throws IOException {
        while(!st.hasMoreTokens()) {
            st=new StringTokenizer(read.readLine());
        }
        return st.nextToken();
    }
    int nextInt() throws IOException {
        return Integer.parseInt(next());
    }
}

2. HJ52 计算字符串的编辑距离

题目

解析

如果arr1[i]!=arr2[2],此时分为3种情况,插入,删除,替换 

最后只需要求它们的最小值即可。

 初始化dp数组时把第一行第一列初始化成其对应的i值,因为当其中一个字符串没有长度时,其只能用插入操作,并且插入另一个字符串的个数。

代码

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {//HJ52 计算字符串的编辑距离
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        char[] arr1=in.next().toCharArray();
        char[] arr2=in.next().toCharArray();
        int n=arr1.length;
        int m=arr2.length;
        //设dp[i][j]为第一个字符串前i个字符和第二个字符串第j个字符的编辑距离。
        //当arr1[i]==arr2[2]时dp[i][j]=dp[i-1][j-1]
        //当arr1[i]!=arr2[2]时
        //如果执行插入操作dp[i][j]=dp[i][j-1]+1
        //如果执行删除操作dp[i][j]=dp[i-1][j]+1
        //如果执行替换操作dp[i][j]=dp[i-1][j-1]+1
        //初始化第一行第一列
        int[][] dp=new int[n+1][m+1];
        //初始化
        for(int i=0;i<=m;i++) {
            dp[0][i]=i;
        }
        //填表加初始化第一列
        for(int i=1;i<=n;i++) {
            dp[i][0]=i;
            for(int j=1;j<=m;j++) {
                if(arr1[i-1]==arr2[j-1]) {
                    dp[i][j]=dp[i-1][j-1];
                }else {
                    dp[i][j]=1+Math.min(Math.min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1]);
                }
            }
        }
        System.out.print(dp[n][m]);
    }
}

空间优化

因为只用得到上一行和当前行的数据,所以只需要两个数组即可。

空间优化代码

import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {//HJ52 计算字符串的编辑距离
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        char[] arr1=in.next().toCharArray();
        char[] arr2=in.next().toCharArray();
        int n=arr1.length;
        int m=arr2.length;
        //设dp[i][j]为第一个字符串前i个字符和第二个字符串第j个字符的编辑距离。
        //当arr1[i]==arr2[2]时dp[j]=dpPrev[j-1]
        //当arr1[i]!=arr2[2]时
        //如果执行插入操作dp[j]=dp[j-1]+1
        //如果执行删除操作dp[j]=dpPrev[j]+1
        //如果执行替换操作dp[j]=dpPrev[j-1]+1
        //初始化第一行第一列
        //设置两个数组,dpPrev是dp的上一行数据
        int[] dp=new int[m+1];
        int[] dpPrev=new int[m+1];
        //初始化
        for(int i=0;i<=m;i++) {
            dpPrev[i]=i;
        }
        //填表加初始化第一列
        for(int i=1;i<=n;i++) {
            dp[0]=i;
            for(int j=1;j<=m;j++) {
                if(arr1[i-1]==arr2[j-1]) {
                    dp[j]=dpPrev[j-1];
                }else {
                    dp[j]=1+Math.min(Math.min(dp[j-1],dpPrev[j]),dpPrev[j-1]);
                }
            }
            dpPrev=Arrays.copyOf(dp,m+1);
        }
        System.out.print(dp[m]);
    }
}

3.AB32 【模板】哈夫曼编码 

题目

解析

哈夫曼编码就不解释是什么意思了,直接百度即可,我们可以在构造树的时候就计算字符串的长度,如图:

也可以用带权路径长度,按正规的方法计算 

代码(构造树时计算)

import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n=in.nextInt();
        PriorityQueue<Long> q=new PriorityQueue();
        for(int i=0;i<n;i++) {
            q.offer(in.nextLong());
        }
        long sum=0;
        while(q.size()>1) {
            Long tem=q.poll()+q.poll();
            sum+=tem;
            q.offer(tem);
        }
        System.out.print(sum);

    }
}

代码(带权路径长度)

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 99715
 * Date: 2024-05-25
 * Time: 17:59
 */
import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main{//哈夫曼编码带权路径长度算法
    static class Node {
        long val;
        Node left;
        Node right;
        Node(long val) {
            this.val=val;
        }
    }
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n=in.nextInt();
        PriorityQueue<Node> q=new PriorityQueue<>((a,b)->{
            return a.val>b.val?1:-1;
        });
        for(int i=0;i<n;i++) {
            q.offer(new Node(in.nextLong()));
        }
        //构造树
        while(q.size()>1) {
            Node a=q.poll();
            Node b=q.poll();
            Node c=new Node(a.val+b.val);
            c.left=a;
            c.right=b;
            q.offer(c);
        }
        //带权路径长度计算
        long sum=kownSum(q.poll(),0);
        System.out.print(sum);
    }
    static long kownSum(Node head,long sum) {
        if(head.left==null&&head.right==null) {
            return sum*head.val;
        }
        //计算左边右边的编号长度
        long left=kownSum(head.left,sum+1);
        long right=kownSum(head.right,sum+1);
        return left+right;
    }
}

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值