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题库
运用到了前缀和 和 记忆化搜索
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)));
}
}