2022年春季学期《算法分析与设计》练习9
问题A :最大子段和
这个题没啥要注意的,就是记得不要和我一样没注意多组输入
AC代码:
1、DP
import java.util.Scanner;
public class Main {
public static int solve(int[] a) {
int maxSum = Integer.MIN_VALUE;
int tempSum = 0;
int begin = 0;
for (int i = 0; i < a.length; i++) {
if (tempSum > 0)
tempSum += a[i];
else {
tempSum = a[i];
begin = i; //标记
}
if (tempSum > maxSum) {
maxSum = tempSum;
//可以在这里获取最佳连续子序列和的起点位置begin和重点位置i
}
}
return maxSum;
}
public static void main(String[] args) {
Scanner sc =new Scanner(System.in);
while(sc.hasNext()){
int n=sc.nextInt();
int[]arr=new int[n];
for(int i=0;i<n;i++){
arr[i]=sc.nextInt();
}
System.out.println(solve(arr));
}
}
}
2、枚举 O(n^2)
import java.util.Scanner;
public class Main {
public static int solve(int[] a) {
int maxSum = Integer.MIN_VALUE;
int begin = 0;
for (int i = 0; i < a.length; i++) {
int tempSum = 0;
for (int j=i;j<a.length;j++){
tempSum+=a[j];
if(tempSum>maxSum){
maxSum=tempSum;
}
}
}
return maxSum;
}
public static void main(String[] args) {
Scanner sc =new Scanner(System.in);
while(sc.hasNext()){
int n=sc.nextInt();
int[]arr=new int[n];
for(int i=0;i<n;i++){
arr[i]=sc.nextInt();
}
System.out.println(solve(arr));
}
}
}
问题B、最大子段和升级版
AC:
import java.util.Scanner;
public class Main {
public static String solve(int[] a) {
int maxSum =Integer.MIN_VALUE;
int tempSum = 0;
int begin = 0,bg2=0;
int end=0;
for (int i = 0; i < a.length; i++) {
if (tempSum > 0)
tempSum += a[i];
else {
tempSum = a[i];
begin = i+1; //标记
}
if (tempSum > maxSum) {
maxSum = tempSum;
end=i+1;
bg2=begin;
//可以在这里获取最佳连续子序列和的起点位置begin和重点位置i
}
}
return maxSum+" "+bg2+" "+end;
}
public static void main(String[] args) {
Scanner sc =new Scanner(System.in);
while(sc.hasNext()){
int n=sc.nextInt();
int[]arr=new int[n];
for(int i=0;i<n;i++){
arr[i]=sc.nextInt();
}
System.out.println(solve(arr));
}
}
}
问题C 、斜线最大最小值
AC:
import java.util.Scanner;
public class Main {
static int L[]=new int[1000]; //用来放每一斜线的最大值
static int S[]=new int[1000];//用来放每一斜线的最小值
static int a[][];
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc=new Scanner(System.in);
while(sc.hasNext()) {
int n=sc.nextInt();
a=new int[n][n];
for(int i=0;i<n;i++) {
for(int j=0;j<n;j++) {
a[i][j]=sc.nextInt();
}
}
for(int r=1;r<=n;r++) {//r表示从主对角线开始第r行
L[r]=a[0][r-1];//初始化
S[r]=a[0][r-1];
for(int p=1;p<=n-r;p++) {
int q=r-1+p;//有逻辑关系q-p=r-1
if(a[p][q]>L[r]) {L[r]=a[p][q];}
if(a[p][q]<S[r]) {S[r]=a[p][q];}
}
}
for(int k=1;k<n+1;k++) {
System.out.println("L"+k+"="+L[k]+", S"+k+"="+S[k]);
}
}
}
}
问题D 、矩阵连乘问题-备忘录法求最优值
AC:
import java.util.Scanner;
public class Main {
static int[][] m;
static int[] p;
private static int lookupChain(int i, int j) {
if(m[i][j]>0) return m[i][j];//表示已经算过就直接用
if(i==j) return 0;//i=j就是一个矩阵,不进行乘法
int u=lookupChain(i,i)+lookupChain(i+1,j)+p[i-1]*p[i]*p[j];//初始化最小值,在i后面分割
for(int k=i+1;k<j;k++) {
int f=lookupChain(i,k)+lookupChain(k+1,j)+p[i-1]*p[k]*p[j];
if(f<u) {u=f;}//有了更小的加括号组合
//m数组是用来保存从第i个矩阵到第j个的最少乘法次数
}
m[i][j]=u;
return u;
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
while(sc.hasNext()) {
int n=sc.nextInt();
p=new int[n];
m=new int[n][n];
for(int i=0;i<n;i++) {
for(int j=0;j<n;j++) {
m[i][j]=0;
}
}//初始化
for(int i=0;i<n;i++) {
p[i]=sc.nextInt();
}
int t=lookupChain(1,n-1);//求的是第一个矩阵到第n-1个矩阵的最小连乘次数
System.out.println(t);
}
}
}
问题E、矩阵连乘问题-动态规划求最优值
AC:
import java.util.Scanner;
public class Main {
static int m[][];
static int p[];
static int n;
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc=new Scanner(System.in);
while(sc.hasNext()) {
n=sc.nextInt();
p=new int[n];
m=new int[n][n];
/*
* for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { m[i][j]=0; } }
*/
for(int i=0;i<n;i++) {
p[i]=sc.nextInt();
}
int t=matrixChain(n,p,m);
System.out.println(t);
}
}
private static int matrixChain(int n,int[] p, int[][] m) {
// TODO Auto-generated method stub
int n1=n-1;//为什么是n-1呢,因为其实m数组是放第几个矩阵乘至第几个矩阵的最小次数,p有n个维数,其实是n-1个矩阵
for(int i=1;i<=n1;i++) {m[i][i]=0;}//主对角线置0,m数组从(1,1)开始放元素
for(int r=2;r<=n1;r++) {//第r行对角线
for(int i=1;i<=n1-r+1;i++) {//每条对角线从第1行到第n-r+1行
int j=r-1+i;
m[i][j]=m[i][i]+m[i+1][j]+p[i-1]*p[i]*p[j];//初始化每个m[i][j]看作最小
for(int k=i+1;k<j;k++) {
int f=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
if(f<m[i][j]) {//找到更小的赋给m[i][j]
m[i][j]=f;
}
}
}
}
return m[1][n1];
}
}
问题F、 矩阵连乘问题-构造最优解
AC:
import java.util.Scanner;
public class Main {
static int m[][],s[][];
static int p[];
static int n;
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc=new Scanner(System.in);
while(sc.hasNext()) {
n=sc.nextInt();
p=new int[n];
m=new int[n][n];
s=new int[n][n];
/*
* for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { m[i][j]=0; } }
*/
for(int i=0;i<n;i++) {
p[i]=sc.nextInt();
}
int t=matrixChain(n,p,m);
// System.out.println(t);
traceback(s,1,n-1);
// for(int i=1;i<n;i++){
// for(int j=1;j<n;j++){
// System.out.print(s[i][j]+" ");
// }
// System.out.println();
// }
}
}
public static void traceback(int [][]s,int i,int j) {
if(i==j) {
return;
}
traceback(s,i,s[i][j]);
traceback(s,s[i][j]+1,j);
System.out.println("A[" + i + ":" + s[i][j]+ "] * A[" + (s[i][j]+1) + ":" +j+ "]");
}
private static int matrixChain(int n,int[] p, int[][] m) {
// TODO Auto-generated method stub
int n1=n-1;//为什么是n-1呢,因为其实m数组是放第几个矩阵乘至第几个矩阵的最小次数,p有n个维数,其实是n-1个矩阵
for(int i=1;i<=n1;i++) {m[i][i]=0;}//主对角线置0,m数组从(1,1)开始放元素
for(int r=2;r<=n1;r++) {//第r行对角线
for(int i=1;i<=n1-r+1;i++) {//每条对角线从第1行到第n-r+1行
int j=r-1+i;
m[i][j]=m[i][i]+m[i+1][j]+p[i-1]*p[i]*p[j];//初始化每个m[i][j]看作最小
s[i][j]=i;
for(int k=i+1;k<j;k++) {
int f=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
if(f<m[i][j]) {//找到更小的赋给m[i][j]
m[i][j]=f;
s[i][j]=k;
}
}
}
}
return m[1][n1];
}
}
问题G 、石子合并问题
注意多组输入!!
package DP;
import java.util.Scanner;
public class stoneMG_dp {
static int arr[];//用来存每一堆石头的数量
static int dp[][];//用来存第i堆加到第j堆的代价,dp[i][i]=0;
static int sum[][];//用来存第i堆到第j堆的和,sum[i][i]等于石堆的数量arr[i];
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
while(sc.hasNext()){
int n=sc.nextInt();
arr=new int[n+1];
dp=new int[n+1][n+1];
sum=new int[n+1][n+1];
for(int i=1;i<n+1;i++){
arr[i]=sc.nextInt();//输入每一个石堆的值
sum[i][i]=arr[i];//sum[i][i]等于石堆的数量arr[i];
}
for(int i=1;i<n+1;i++){
for(int j=i+1;j<n+1;j++){
sum[i][j]=sum[i][j-1]+arr[j];//sum[i][j]表示的是从第i堆到第j堆之间求和的结果
}
}
//至此,准备工作已就绪
//dp开始
solve(n);
//输出右上角
System.out.println(dp[1][n]);
}
sc.close();
}
static void solve(int n){
for(int i=1;i<n+1;i++){
dp[i][i]=0;//填第一条对角线
}
for(int r=2;r<=n;r++){//一共有n条对角线,上面的for里面填了第一条,这里从第二条开始
for(int i=1;i<=n-r+1;i++){//对对角线遍历,对于第r条对角线,它所跨的行数是n-r+1(规律)
//第r条对角线上列(j)与行(i)的规律j-i=r-1;
int j=i+r-1;
dp[i][j]=dp[i][i]+dp[i+1][j]+sum[i][j];//默认先从i后面分开
for(int k=i+1;k<j;k++){//逐点分开找到最小的那个
dp[i][j]=Math.min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[i][j]);
}
}
}
}
}
问题H 、XP的矩阵
import java.util.Scanner;
public class XP_juzheng {
static int[][]arr,min;
static int M,N;
static void solve(){
min[1][1]=arr[1][1];
for(int i=2;i<=M;i++){
min[i][1]=arr[i][1]+min[i-1][1];
}
for(int j=2;j<=N;j++){
min[1][j]=arr[1][j]+min[1][j-1];
}
for(int i=2;i<=M;i++){
for(int j=2;j<=N;j++){
min[i][j]=Math.min(min[i][j-1],min[i-1][j])+arr[i][j];
}
}
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int t=sc.nextInt();
while(t>0){
--t;
arr=new int[1010][1010];
min=new int[1010][1010];
M=sc.nextInt();
N=sc.nextInt();
for(int i=1;i<=M;i++){
for(int j=1;j<=N;j++){
arr[i][j]=sc.nextInt();
}
}
solve();
System.out.println(min[M][N]);
// for(int i=1;i<=M;i++){
// for(int j=1;j<=N;j++){
// System.out.print(min[i][j]+" ");
// }
// System.out.println();
// }//这个用来检测是否正确
}
}
}
写在最后:慢慢来,总会遇到困难的,慢慢来。