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;
}
}