2024-02-18(区间DP)

282. 石子合并 - AcWing题库的扩展2024-02-06(线性DP、区间DP)-CSDN博客

1068. 环形石子合并 - AcWing题库

先把环形展开,形成一条链,再复制一遍。

一般有两种写法:迭代式和记忆化搜索

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int N = 410, INF = 0x3f3f3f3f;
        int[] w = new int[N];//石子
        int[] s = new int[N];//前缀和
        int[][] f = new int[N][N];//最大值
        int[][] g = new int[N][N];//最小值
        
        int n = sc.nextInt();
        for(int i = 1; i <= n; i ++){
            w[i] = sc.nextInt();
            w[i + n] = w[i];//第二段链表的值,再重复一遍
        }
        
        for(int i = 1; i <= n * 2; i ++){//注意范围,求前缀和
            s[i] = s[i - 1] + w[i];
        }
        
        for(int i = 0; i < N; i ++){
            Arrays.fill(f[i], - INF);//要求最大值,先初始化为负无穷
            Arrays.fill(g[i], INF);//要求最小值,先初始化为正无穷
        }
        
        //环形迭代式模板
        for(int len = 1; len <= n; len ++){//遍历长度
            for(int l = 1; l + len - 1 <= 2 * n; l ++){//遍历左端点
                int r = l + len - 1;//表示出右端点
                if(len == 1) f[l][r] = g[l][r] = 0;//如果只有一堆
                else{
                    for(int k = l; k < r; k ++){//dp过程,遍历分割点
                        f[l][r] = Math.max(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
                        g[l][r] = Math.min(g[l][r], g[l][k] + g[k + 1][r] + s[r] - s[l - 1]);
                    }
                }
            }
        }
        
        int max = - INF;
        int min = INF;
        for(int i = 1; i <= n; i ++){//遍历得到最大最小值
            max = Math.max(max, f[i][i + n - 1]);
            min = Math.min(min, g[i][i + n - 1]);
        }
        
        System.out.println(min);
        System.out.println(max);
    }
}

 

320. 能量项链 - AcWing题库

状态转移方程:

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int N = 210;
        int[][] f = new int[N][N];
        int[] w = new int[N];
        
        int n = sc.nextInt();
        for(int i = 1; i <= n; i ++){
            w[i] = sc.nextInt();
            w[i + n] = w[i];
        }
        
        for(int len = 3; len <= n + 1; len ++){//每一段能合并的话,必须有三个值
            for(int l = 1; l + len - 1 <= 2 * n; l ++){//l是左端点,l+len-1是右端点
                int r = l + len - 1;
                //分割点从左端点的后一位开始,也不能是右端点
                for(int k = l + 1; k < r; k ++){
                    //状态转移方程
                    f[l][r] = Math.max(f[l][r], f[l][k] + f[k][r] + w[l] * w[r] * w[k]);
                }
            }
        }
        
        int max = 0;//枚举找到最大值
        for(int i = 1; i <= n; i ++){
            max = Math.max(max, f[i][i + n]);
        }
        System.out.print(max);
    }
}

 

1069. 凸多边形的划分 - AcWing题库

 

高精度    算法篇——高精度(Java)_java高精度-CSDN博客
加add()   减subtract()   乘multiply()   除divide()   两者取最小min()   两者取最大max() 

import java.util.*;
import java.math.BigInteger;

//高精度
//加add() 减subtract() 乘multiply() 除divide() 两者取最小min() 两者取最大max()
public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int N = 55;
        String INF = "1000000000000000000000000000000";//高精度的正无穷
        BigInteger[] w = new BigInteger[N];
        BigInteger[][] f = new BigInteger[N][N];
        
        int n = sc.nextInt();
        for(int i = 1; i <= n; i ++){
            w[i] = sc.nextBigInteger();
        }
        
        //BigInteger创建之后默认值为null,需要初始化为0
        for(int i = 0; i < N; i ++){
            for(int j = 0; j < N; j ++){
                f[i][j] = new BigInteger("0");
            }
        }
        
        for(int len = 3; len <= n; len ++){
            for(int l = 1; l + len - 1 <= n; l ++){
                int r = l + len - 1;
                f[l][r] = new BigInteger(INF);//因为求的是最小值,所以初始化为正无穷
                for(int k = l + 1; k < r; k ++){
                    BigInteger t = w[l].multiply(w[r]).multiply(w[k]);
                    f[l][r] = f[l][r].min((f[l][k].add(f[k][r]).add(t)));
                }
            }
        }
        
        System.out.print(f[1][n]);
    }
}

 

479. 加分二叉树 - AcWing题库

其中状态划分的依据就是哪个点是根节点 

import java.util.*;

public class Main{
    static int N = 35;
    static int[] w = new int[N];//每个结点的分数
    static int[][] f = new int[N][N];//所有中序遍历是[L,R]的最大值
    static int[][] g = new int[N][N];//存每一个个结点
    
    public static void dfs(int l, int r){
        if(l > r) return;//区间左端点大于右端点,直接返回
        else{
            int root = g[l][r];//根节点
            System.out.print(root + " ");
            dfs(l, root - 1);//根节点的左子树
            dfs(root + 1, r);//根节点的右子树
        }
    }
    
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        
        for(int i = 1; i <= n; i ++){
            w[i] = sc.nextInt();//每个结点的分数
        }
        
        for(int len = 1; len <= n; len ++){//遍历每个结点
            for(int l = 1; l + len - 1 <= n; l ++){//左端点
                int r = l + len - 1;//右端点
                if(len == 1){//如果区间长度为1
                    f[l][r] = w[l];//直接是节点分数
                    g[l][r] = l;//根节点就是区间左端点
                }else{
                    for(int k = l; k < r; k ++){
                        int left = (k == l ? 1 : f[l][k - 1]);//如果k等于l,那么就没有左子树,分数为1
                        int right = (k == r ? 1 : f[k + 1][r]);//同上
                        int score = left * right + w[k];//题目所给公式
                        if(f[l][r] < score){//要求最小值
                            f[l][r] = score;//重新赋值
                            g[l][r] = k;//为遍历到的根节点
                        }
                    }
                }
            }
        }
        System.out.println(f[1][n]);
        dfs(1, n);//前序遍历
    }
}

 

321. 棋盘分割 - AcWing题库

运用到了前缀和 和 记忆化搜索

 前缀和:AcWing 796. 子矩阵的和 - AcWing

import java.util.*;

public class Main{
    static int N = 15, M = 9, INF = 0x3f3f3f3f;
    static double[][][][][] f = new double[M][M][M][M][N];//将子矩阵(x1, y1)(x2, y2)切分成k部分的所有方案
    static int n, m = 8;//n是输入的值,m是行数列数
    static double X;//x拔,平均值那个,这是固定的,是不变量,要提前算出来
    static double[][] s = new double[M][M];//矩阵和,用二维前缀和来算
    
    //用来算矩阵的均方差
    public static double get(int x1, int y1, int x2, int y2){
        double sum = s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] - X;
        return sum * sum / n;
    }
    
    //dp过程,记忆化搜索
    public static double dp(int x1, int y1, int x2, int y2, int k){
        double res = f[x1][y1][x2][y2][k];
        
        if(res >= 0) return res;//如果当前这个f大于等于0,就说明已经计算过了,不需要重复计算,直接返回原来的值
        if(k == 1) return get(x1, y1, x2, y2);//如果k等于1,说明到了最后一块,直接均方值返回值
        
        res = INF;//否则将res置为无穷大(因为要求最小值)
        
        //横切
        for(int i = x1; i < x2; i ++){//i表示的是切在哪里
            //切完之后,分成两部分,一部分用来继续切割,一部分直接加上值
            res = Math.min(res, dp(x1, y1, i, y2, k - 1) + get(i + 1, y1, x2, y2));//取上面部分进行再分割
            res = Math.min(res, dp(i + 1, y1, x2, y2, k - 1) + get(x1, y1, i, y2));//取下面部分进行再分割
        }
        
        //res要经过横切和纵切,找到最小的那个
        //竖切
        for(int i = y1; i < y2; i ++){
            res = Math.min(res, dp(x1, y1, x2, i, k - 1) + get(x1, i + 1, x2, y2));
            res = Math.min(res, dp(x1, i + 1, x2, y2, k - 1) + get(x1, y1, x2, i));
        }
        
        //将res的值赋给f[x1][y1][x2][y2][k],然后return
        return f[x1][y1][x2][y2][k] = res;
    }
    
    //开始main函数
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        
        for(int i = 1; i <= m; i ++){
            for(int j = 1; j <= m; j ++){
                s[i][j] = sc.nextInt();//录入数据
                s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];//二维前缀和求出方格的和
            }
        }
        
        for(int i = 0; i < M; i ++){
            for(int j = 0; j < M; j ++){
                for(int k = 0; k < M; k ++){
                    for(int s = 0; s < M; s ++){
                        Arrays.fill(f[i][j][k][s], -1);//全部初始化为-1,说明还没有被计算过
                    }
                }
            }
        }
        
        X = (double) s[m][m] / n;//计算X拔
        
        System.out.printf("%.3f",Math.sqrt(dp(1, 1, m, m, n)));
    }
}

 

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值