提交 9f2a7a56 编写于 作者: qq_36480062's avatar qq_36480062

c

上级 1717b198
...@@ -3,20 +3,7 @@ package dp.树形dp; ...@@ -3,20 +3,7 @@ package dp.树形dp;
import java.util.Scanner; import java.util.Scanner;
/** /**
* 有n个物品和容量为V的背包 * 有 N 个物品和一个容量是 V的背包。
* 物品之间有依赖关系,关系组成一颗树的形状,
* 如果要选一个物品,则必选他的父节点
* 求最大价值
* dfs+dp dp.树形dp
* 状态定义:集合f[u,j]:所有从以u为根的子树中选,且总体积不超过j的选法
* 属性:max最大价值
* 集合划分:子树1,子树3,子树3
* 再根据体积划分子树:体积是0-m 有m+1总可能
* 用一个数字表示一类方案
* 把每一颗子树看做物品组,分组背包问题
* 链式前向星建图
* 有 N 个物品和一个容量是 V
* 的背包。
* 物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。 * 物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。
* 如下图所示: * 如下图所示:
* 如果选择物品5,则必须选择物品1和2。这是因为2是5的父节点,1是2的父节点。 * 如果选择物品5,则必须选择物品1和2。这是因为2是5的父节点,1是2的父节点。
...@@ -49,6 +36,20 @@ import java.util.Scanner; ...@@ -49,6 +36,20 @@ import java.util.Scanner;
* 3 6 2 * 3 6 2
* 输出样例: * 输出样例:
* 11 * 11
* <p>
* 有n个物品和容量为V的背包
* 物品之间有依赖关系,关系组成一颗树的形状,
* 如果要选一个物品,则必选他的父节点
* 求最大价值
* dfs+dp dp.树形dp
* 状态定义:集合f[u,j]:所有从以u为根的子树中选,且总体积不超过j的选法
* 属性:max最大价值
* 集合划分:子树1,子树3,子树3
* 划分依据:体积为0~m的子树,把每一棵子树看做一个物品做
* 再根据体积划分子树:体积是0-m 有m+1总可能
* 用一个数字表示一类方案
* 把每一颗子树看做物品组,分组背包问题
* 链式前向星建树
*/ */
public class 有依赖的背包问题 { public class 有依赖的背包问题 {
...@@ -62,7 +63,7 @@ public class 有依赖的背包问题 { ...@@ -62,7 +63,7 @@ public class 有依赖的背包问题 {
w[i] = sc.nextInt(); w[i] = sc.nextInt();
p = sc.nextInt(); p = sc.nextInt();
if (p == -1) root = i; if (p == -1) root = i;
else add(p, i); else add(p, i);//p向i连一条边,父节点向子节点连边
} }
dfs(root); dfs(root);
System.out.println(f[root][m]); System.out.println(f[root][m]);
...@@ -74,7 +75,7 @@ public class 有依赖的背包问题 { ...@@ -74,7 +75,7 @@ public class 有依赖的背包问题 {
dfs(e[i]); dfs(e[i]);
//以u为根的子树,每一棵子树都是一个分组背包问题 //以u为根的子树,每一棵子树都是一个分组背包问题
//抽象成2层 //抽象成2层
for (int j = m - v[u]; j >= 0; j--) {//枚举体积 for (int j = m - v[u]; j >= 0; j--) {//枚举体积,因为根节点必须要选,预留m-v[u
for (int k = 0; k <= j; k++) {//枚举决策,分为以0~m的一些决策 for (int k = 0; k <= j; k++) {//枚举决策,分为以0~m的一些决策
f[u][j] = Math.max(f[u][j], f[u][j - k] + f[son][k]); f[u][j] = Math.max(f[u][j], f[u][j - k] + f[son][k]);
//以u为根节点,在剩余体积为0~m-v[u]的情况下,选子树 //以u为根节点,在剩余体积为0~m-v[u]的情况下,选子树
...@@ -95,11 +96,11 @@ public class 有依赖的背包问题 { ...@@ -95,11 +96,11 @@ public class 有依赖的背包问题 {
static int N = 110; static int N = 110;
static int[] head = new int[N], e = new int[N], ne = new int[N]; static int[] head = new int[N], e = new int[N], ne = new int[N];
static int n, m, cnt = 1; static int n, m, idx = 1;
static void add(int a, int b) { static void add(int a, int b) {
e[cnt] = b; e[idx] = b;
ne[cnt] = head[a]; ne[idx] = head[a];
head[a] = cnt++; head[a] = idx++;
} }
} }
...@@ -29,7 +29,7 @@ import static java.lang.Math.min; ...@@ -29,7 +29,7 @@ import static java.lang.Math.min;
* 18 * 18
* 状压dp * 状压dp
* 该图是完全图 * 该图是完全图
* 2的整数次幂-1的二进制位全是1111111 * 2的整数次幂-1的二进制位全是1
* f[i,j]状态表示为从0走到顶点j状态为i的所有走法 * f[i,j]状态表示为从0走到顶点j状态为i的所有走法
*/ */
public class 哈密尔顿回路 { public class 哈密尔顿回路 {
......
...@@ -39,10 +39,9 @@ import java.util.Scanner; ...@@ -39,10 +39,9 @@ import java.util.Scanner;
* 另外,a中的合法状态可以转移到哪些合法的状态,也可以预处理出来存进向量b中。 * 另外,a中的合法状态可以转移到哪些合法的状态,也可以预处理出来存进向量b中。
* 本题方案数可能很大,需要用long long存储。 * 本题方案数可能很大,需要用long long存储。
* <p> * <p>
* f[i,j,k] * f[i,j,k] k为二进制数,表示在哪里放了国王
* 状态定义:所有只从前i行摆放,已经摆了j个国王,并且第i行的摆放状态是k的所有方案的集合 * 状态定义:所有只从前i行摆放,已经摆了j个国王,并且第i行的摆放状态是k的所有方案的集合
* 属性count * 属性count
*
*/ */
public class 骑士 { public class 骑士 {
public static void main(String[] args) { public static void main(String[] args) {
...@@ -52,7 +51,7 @@ public class 骑士 { ...@@ -52,7 +51,7 @@ public class 骑士 {
for (int i = 0; i < 1 << n; i++) { for (int i = 0; i < 1 << n; i++) {
if (check(i)) { if (check(i)) {
a.add(i);//去除相邻的放置方法 a.add(i);//去除相邻的放置方法
cnt.add(count(i)); cnt.add(count(i));//预处理i作为
} }
} }
int s = 0, t = 0, u = 0; int s = 0, t = 0, u = 0;
......
...@@ -88,7 +88,12 @@ public class 大盗阿福 { ...@@ -88,7 +88,12 @@ public class 大盗阿福 {
* 状态转移,看成图论问题 * 状态转移,看成图论问题
* f[i,0] f[i,1] 表示:所有走了i步,且当前位于状态j的所有走法 * f[i,0] f[i,1] 表示:所有走了i步,且当前位于状态j的所有走法
* 属性:max * 属性:max
* 状态计算:f[i,0] 0->0 上一个不偷,这个一个也不偷, f[i-1,0] * 状态0代表没偷,状态1代表偷了
* 0->0
* 0->1
* 1->0
* 只有这三种转移方式
* 状态计算:f[i,0] 0->0 上一个不偷,这个一个也不偷, 对应就是f[i-1,0]
* 1->0 上一个偷了,这个不偷 f[i-1,1] * 1->0 上一个偷了,这个不偷 f[i-1,1]
* f[i,1] 状态计算,只能0->1 上个不偷这个偷,f[i-1,0]+w[i] * f[i,1] 状态计算,只能0->1 上个不偷这个偷,f[i-1,0]+w[i]
*/ */
......
...@@ -47,6 +47,8 @@ import java.util.Scanner; ...@@ -47,6 +47,8 @@ import java.util.Scanner;
* f[i][j][1] = f[i-1][j][1],故f[i][j][1] = max(f[i-1][j][1],f[i-1][j-1][0]-w[i])。 * f[i][j][1] = f[i-1][j][1],故f[i][j][1] = max(f[i-1][j][1],f[i-1][j-1][0]-w[i])。
* 从而状态转移方程就求出来了,下面考虑边界状态,f[i][0][0]表示第i天都未进行交易, * 从而状态转移方程就求出来了,下面考虑边界状态,f[i][0][0]表示第i天都未进行交易,
* 收益是0,除此之外,f[i][[j][0]与f[i][0][1]的初始状态都应该是不合法的,设置为-INF。 * 收益是0,除此之外,f[i][[j][0]与f[i][0][1]的初始状态都应该是不合法的,设置为-INF。
* <p>
* 引入一层状态用来存交易次数
*/ */
public class 股票买卖4 { public class 股票买卖4 {
public static void main(String[] args) { public static void main(String[] args) {
...@@ -68,7 +70,7 @@ public class 股票买卖4 { ...@@ -68,7 +70,7 @@ public class 股票买卖4 {
for (int i = 0; i <= n; i++) { for (int i = 0; i <= n; i++) {
for (int j = 0; j <= k; j++) { for (int j = 0; j <= k; j++) {
for (int l = 0; l < 2; l++) { for (int l = 0; l < 2; l++) {
f[i][j][l] = Integer.MIN_VALUE / 2; f[i][j][l] = -0x3f3f3f3f;
} }
} }
}//初始化负无穷 }//初始化负无穷
...@@ -79,10 +81,12 @@ public class 股票买卖4 { ...@@ -79,10 +81,12 @@ public class 股票买卖4 {
f[i][j][0] = Math.max(f[i - 1][j][0], f[i - 1][j][1] + a[i]); f[i][j][0] = Math.max(f[i - 1][j][0], f[i - 1][j][1] + a[i]);
//卖出不消耗次数 //卖出不消耗次数
f[i][j][1] = Math.max(f[i - 1][j][1], f[i - 1][j - 1][0] - a[i]); f[i][j][1] = Math.max(f[i - 1][j][1], f[i - 1][j - 1][0] - a[i]);
//昨天买的,今天不, 昨天交易次数为j-1没买,今天买上 //昨天买的,今天不, 昨天交易次数为j-1没买,今天买上
//买入消耗次数 //买入消耗次数
} }
} }
//查看进行i次交易能得到利润最大值
int res = 0; int res = 0;
for (int i = 0; i <= k; i++) { for (int i = 0; i <= k; i++) {
res = Math.max(res, f[n][i][0]); res = Math.max(res, f[n][i][0]);
......
...@@ -46,7 +46,7 @@ public class 买书 { ...@@ -46,7 +46,7 @@ public class 买书 {
} }
static int n = 400; static int n = 400;
static int v[] = {0, 10, 20, 50, 100}; static int[] v = {0, 10, 20, 50, 100};
static int[] dp = new int[1010]; static int[] dp = new int[1010];
static int[][] f = new int[5][1010]; static int[][] f = new int[5][1010];
} }
...@@ -5,8 +5,9 @@ import java.util.Scanner; ...@@ -5,8 +5,9 @@ import java.util.Scanner;
/** /**
* 有N组物品,每个组只能选1个, * 有N组物品,每个组只能选1个,
* 求最大价值 * 求最大价值
* 转换成01背包问题, * 类似01背包问题,
* 状态定义为f[i,j]:前i组物品可选,体积不超过j的所有选法最大值 * 状态定义为f[i,j]:前i组物品可选,体积不超过j的所有选法最大值
* 属性max
* 划分依据,选第i组物品的第几个,还是不选第i组物品 * 划分依据,选第i组物品的第几个,还是不选第i组物品
* 状态划分:选第i组物品的第1个,选第i组物品的第2个,选第i组物品的第s[i]个,不选第i组物品 * 状态划分:选第i组物品的第1个,选第i组物品的第2个,选第i组物品的第s[i]个,不选第i组物品
* 状态计算:显然不选第i组物品就是f[i-1,j] * 状态计算:显然不选第i组物品就是f[i-1,j]
......
...@@ -5,8 +5,10 @@ import java.util.Scanner; ...@@ -5,8 +5,10 @@ import java.util.Scanner;
/** /**
* 完全背包变形多重背包:每样物品最多选s[i]个 * 完全背包变形多重背包:每样物品最多选s[i]个
* 同理状态定义:f[i,j]代表体积为j前i个物品可选的最大价值 * 同理状态定义:f[i,j]代表体积为j前i个物品可选的最大价值
* 划分依据:选第i个物品多少个
* 状态划分:选0个第i个物品,选1个第i个物品,选2个第i个物品,选3个第i个物品....选s[i]个第i个物品 * 状态划分:选0个第i个物品,选1个第i个物品,选2个第i个物品,选3个第i个物品....选s[i]个第i个物品
* 状态计算,不失一般性,选k个:f[i-1,j-k*v[i]]+w[i] 只有在j>=k*v[i]并且k<=s[i]的时候才合法 * 状态计算,不失一般性,选k个:f[i-1,j-k*v[i]]+w[i] 只有在j>=k*v[i]并且k<=s[i]的时候才合法
* O(n^3)
*/ */
public class 多重背包1 { public class 多重背包1 {
public static void main(String[] args) { public static void main(String[] args) {
......
...@@ -17,6 +17,32 @@ import java.util.Scanner; ...@@ -17,6 +17,32 @@ import java.util.Scanner;
*/ */
public class 多重背包二进制优化 { public class 多重背包二进制优化 {
public static void main(String[] args) { public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
N = sc.nextInt();
V = sc.nextInt();
int a, b, c;
for (int i = 1; i <= N; i++) {
v[i] = sc.nextInt();
w[i] = sc.nextInt();
s[i] = sc.nextInt();
}
for (int i = 1; i <= N; i++) {
for (int k = 1; k <= s[i]; k *= 2) {//直接变成01背包
for (int j = V; j >= v[i]; j--) {
dp[j] = Math.max(dp[j - v[i]] + w[i], dp[j]);
}
s[i] -= k;
}
if (s[i] > 0) {
for (int j = V; j >= v[i]; j--) {
dp[j] = Math.max(dp[j - v[i]] + w[i], dp[j]);
}
}
}
System.out.println(dp[V]);
}
static void yuchuli() {
Scanner sc = new Scanner(System.in); Scanner sc = new Scanner(System.in);
N = sc.nextInt(); N = sc.nextInt();
V = sc.nextInt(); V = sc.nextInt();
...@@ -38,7 +64,7 @@ public class 多重背包二进制优化 { ...@@ -38,7 +64,7 @@ public class 多重背包二进制优化 {
v[cnt] = a * c; v[cnt] = a * c;
w[cnt] = b * c; w[cnt] = b * c;
} }
} }//二进制预处理
N = cnt;//二进制优化后,一共有n个物品,01背包 N = cnt;//二进制优化后,一共有n个物品,01背包
for (int i = 1; i <= N; i++) { for (int i = 1; i <= N; i++) {
for (int j = V; j >= v[i]; j--) { for (int j = V; j >= v[i]; j--) {
...@@ -48,8 +74,9 @@ public class 多重背包二进制优化 { ...@@ -48,8 +74,9 @@ public class 多重背包二进制优化 {
System.out.println(dp[V]); System.out.println(dp[V]);
} }
static int cnt=0; static int cnt = 0;
static int dp[] = new int[2010]; static int[] dp = new int[2010];
static int[] v = new int[1000 * 13], w = new int[1000 * 13]; static int[] v = new int[1000 * 13], w = new int[1000 * 13];
static int[] s = new int[13000];
static int N, V; static int N, V;
} }
...@@ -23,11 +23,15 @@ import java.util.Scanner; ...@@ -23,11 +23,15 @@ import java.util.Scanner;
* M看做背包容量,每一个数看作物品,把Ai看做体积 * M看做背包容量,每一个数看作物品,把Ai看做体积
* 求出总体积为M的所有方案数量 * 求出总体积为M的所有方案数量
* 状态定义:f[i,j],集合:所有只从前i个物品选,且恰好总体积是j的方案总和 * 状态定义:f[i,j],集合:所有只从前i个物品选,且恰好总体积是j的方案总和
* 属性:count * 属性:count,方案数
* 状态划分:包含第i个,不包含第i个 * 状态划分:包含第i个,不包含第i个
* 划分依据:包不包含当前物品i
* 状态计算:不包含第i个->f[i-1,j] * 状态计算:不包含第i个->f[i-1,j]
* 包含第i个->f[i-1,j-v[i]] * 包含第i个->f[i-1,j-v[i]]
* 状态合并: * 状态合并:
* 显然:从前i个物品选且总体积恰好为j方案应当等于如下
* 前i-1种物品且总体积为j的方案加上前i-1种物品可选总体积恰好为j-v[i]
* 因为前i-1种物品可选总体积恰好为j-v[i]再加上第重量为v[i]的第i种物品恰好是一种合法方案
* 所以f[i,j]=f[i-1,j]+f[i-1,j-v[i]] * 所以f[i,j]=f[i-1,j]+f[i-1,j-v[i]]
*/ */
public class 数字组合 { public class 数字组合 {
...@@ -36,15 +40,15 @@ public class 数字组合 { ...@@ -36,15 +40,15 @@ public class 数字组合 {
n = sc.nextInt(); n = sc.nextInt();
m = sc.nextInt(); m = sc.nextInt();
for (int i = 1; i <= n; i++) { for (int i = 1; i <= n; i++) {
A[i] = sc.nextInt(); v[i] = sc.nextInt();
} }
// one(); // one();
f[0][0] = 1; f[0][0] = 1;
for (int i = 1; i <= n; i++) { for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) { for (int j = 0; j <= m; j++) {
f[i][j] = f[i - 1][j]; f[i][j] = f[i - 1][j];
if (j >= A[i]) if (j >= v[i])
f[i][j] += f[i - 1][j - A[i]]; f[i][j] += f[i - 1][j - v[i]];
} }
} }
System.out.println(f[n][m]); System.out.println(f[n][m]);
...@@ -54,14 +58,14 @@ public class 数字组合 { ...@@ -54,14 +58,14 @@ public class 数字组合 {
static void one() { static void one() {
dp[0] = 1; dp[0] = 1;
for (int i = 1; i <= n; i++) { for (int i = 1; i <= n; i++) {
for (int j = m; j >= A[i]; j--) { for (int j = m; j >= v[i]; j--) {
dp[j] += dp[j - A[i]]; dp[j] += dp[j - v[i]];
} }
} }
System.out.println(dp[m]); System.out.println(dp[m]);
} }
static int[] A = new int[110]; static int[] v = new int[110];
static int[][] f = new int[110][10010]; static int[][] f = new int[110][10010];
static int[] dp = new int[10010]; static int[] dp = new int[10010];
static int n, m; static int n, m;
......
...@@ -30,7 +30,7 @@ import java.util.Scanner; ...@@ -30,7 +30,7 @@ import java.util.Scanner;
* 输出样例: * 输出样例:
* 8 * 8
* 分析:背包有重量和体积限制,每个物品可选可不选,求最大价值 * 分析:背包有重量和体积限制,每个物品可选可不选,求最大价值
* 状态表示:f[i,j,k] 所有只从前i种物品选,总体积超过j,总重量不超过k的选法 * 状态表示:f[i,j,k] 所有只从前i种物品选,总体积超过j,总重量不超过k的选法
* 属性: max 最大价值 * 属性: max 最大价值
* 状态划分:所有包含第i个物品的选法,所有不包含物品i的选法 * 状态划分:所有包含第i个物品的选法,所有不包含物品i的选法
* 不选第i个物品:f[i-1,j,k] * 不选第i个物品:f[i-1,j,k]
......
...@@ -29,12 +29,14 @@ import java.util.Scanner; ...@@ -29,12 +29,14 @@ import java.util.Scanner;
* 2 1 * 2 1
* 3 1 * 3 1
* 分析: * 分析:
* 抽象为分组背包问题
* 本题相当于AcWing 9 分组背包问题问题。每组物品要么一个不选, * 本题相当于AcWing 9 分组背包问题问题。每组物品要么一个不选,
* 要么同一组最多选择其中的一个,本题每个公司可获得的设备数量也是只能选择一个, * 要么同一组最多选择其中的一个,本题每个公司可获得的设备数量也是只能选择一个,
* 故状态表示f[i][j]表示在前i家公司中选择分配不超过j台设备的最大盈利。 * 故状态表示f[i][j]表示在前i家公司中选择分配不超过j台设备的最大盈利。
* 状态转移方程为f[i][j] = max(f[i-1][j-k]+w[i][k]),其中j >= k。 * 状态转移方程为f[i][j] = max(f[i-1][j-k]+w[i][k]),其中j >= k。
* 另外题目要求输出任意一组合法方案,所以用个数组存储方案即可,具体第i组物品选与不选, * 另外题目要求输出任意一组合法方案,所以用个数组存储方案即可,具体第i组物品选与不选,
* 如果选,选几个,只需要找出其中的一个k,使得f[i][j] == f[i-1][j-k]+w[i][k]即可使得第i个物品选k个是合法的方案。 * 如果选,选几个,只需要找出其中的一个k,
* 使得f[i][j] == f[i-1][j-k]+w[i][k]即可使得第i个物品选k个是合法的方案。
*/ */
public class 机器分配 { public class 机器分配 {
public static void main(String[] args) { public static void main(String[] args) {
...@@ -59,8 +61,9 @@ public class 机器分配 { ...@@ -59,8 +61,9 @@ public class 机器分配 {
int j = m; int j = m;
for (int i = n; i != 0; i--) {//倒序往前找决策 for (int i = n; i != 0; i--) {//倒序往前找决策
for (int k = 0; k <= j; k++) { for (int k = 0; k <= j; k++) {
//k从0开始使得存在不选第i组物品的方案
if (f[i][j] == f[i - 1][j - k] + w[i][k]) { if (f[i][j] == f[i - 1][j - k] + w[i][k]) {
way[i] = k; way[i] = k;//这个是倒序存的,所以最终可以正序输出
j -= k; j -= k;
break; break;
} }
......
...@@ -33,12 +33,13 @@ public class 混合背包 { ...@@ -33,12 +33,13 @@ public class 混合背包 {
t[i] = sc.nextInt(); t[i] = sc.nextInt();
} }
for (int i = 1; i <= N; i++) { for (int i = 1; i <= N; i++) {
if (t[i] == 0) { if (t[i] == 0) {//完全背包
for (int j = v[i]; j <= V; j++) { for (int j = v[i]; j <= V; j++) {
f[j] = Math.max(f[j], f[j - v[i]] + w[i]); f[j] = Math.max(f[j], f[j - v[i]] + w[i]);
} }
} else { } else {
if (t[i] == -1) t[i] = 1; if (t[i] == -1) t[i] = 1;//01背包
//多重背包转01背包
for (int k = 1; k <= t[i]; k *= 2) { for (int k = 1; k <= t[i]; k *= 2) {
for (int j = V; j >= k * v[i]; j--) {//把k个物品看成可选可不选的一个物品,01背包 for (int j = V; j >= k * v[i]; j--) {//把k个物品看成可选可不选的一个物品,01背包
f[j] = Math.max(f[j], f[j - k * v[i]] + w[i] * k); f[j] = Math.max(f[j], f[j - k * v[i]] + w[i] * k);
......
...@@ -35,6 +35,7 @@ import java.util.Scanner; ...@@ -35,6 +35,7 @@ import java.util.Scanner;
* 4 20 119 * 4 20 119
* 【输出样例】 * 【输出样例】
* 249 * 249
* 最低状态定义稍有区别,状态定义恰好和至少的区别做法就不一样
* 显然三个东西,看如何划分: * 显然三个东西,看如何划分:
* 物品: 氧气 氮气 重量 * 物品: 氧气 氮气 重量
* a b c * a b c
...@@ -51,6 +52,7 @@ import java.util.Scanner; ...@@ -51,6 +52,7 @@ import java.util.Scanner;
* 在状态定义恰好是j,恰好是k的时候 * 在状态定义恰好是j,恰好是k的时候
* f[0,0,0]=0 是合法的 * f[0,0,0]=0 是合法的
* f[0,j,k]=是非法的,因为只选0个不可能达到恰好是j,或者k * f[0,j,k]=是非法的,因为只选0个不可能达到恰好是j,或者k
* 应该f[0,j,k]=正无穷
*/ */
public class 潜水员二维费用背包 { public class 潜水员二维费用背包 {
public static void main(String[] args) { public static void main(String[] args) {
...@@ -59,27 +61,27 @@ public class 潜水员二维费用背包 { ...@@ -59,27 +61,27 @@ public class 潜水员二维费用背包 {
m = sc.nextInt();//需要的氮气 m = sc.nextInt();//需要的氮气
shu = sc.nextInt();//有多少个气缸 shu = sc.nextInt();//有多少个气缸
for (int i = 1; i <= shu; i++) { for (int i = 1; i <= shu; i++) {
o[i] = sc.nextInt(); v1[i] = sc.nextInt();
d[i] = sc.nextInt(); v2[i] = sc.nextInt();
w[i] = sc.nextInt(); w[i] = sc.nextInt();
} }
two(); two();
} }
static int n, m, shu; static int n, m, shu;
static int[] o = new int[1010], d = new int[1010], w = new int[1010]; static int[] v1 = new int[1010], v2 = new int[1010], w = new int[1010];
static int[][][] f = new int[23][88][1010]; static int[][][] f = new int[23][88][1010];
static int[][] dp = new int[5000][1600]; static int[][] dp = new int[5000][1600];
static void two() { static void two() {
for (int i = 0; i < dp.length; i++) { for (int[] ints : dp) {
Arrays.fill(dp[i], Integer.MAX_VALUE - 10000); Arrays.fill(ints, 0x3f3f3f3f);
} }
dp[0][0] = 0; dp[0][0] = 0;
for (int i = 1; i <= shu; i++) { for (int i = 1; i <= shu; i++) {
for (int j = n; j >= 0; j--) { for (int j = n; j >= 0; j--) {
for (int k = m; k >= 0; k--) { for (int k = m; k >= 0; k--) {
dp[j][k] = Math.min(dp[j][k], dp[Math.max(j - o[i], 0)][Math.max(k - d[i], 0)] + w[i]); dp[j][k] = Math.min(dp[j][k], dp[Math.max(j - v1[i], 0)][Math.max(k - v2[i], 0)] + w[i]);
} }
} }
} }
......
...@@ -28,9 +28,15 @@ import java.util.Scanner; ...@@ -28,9 +28,15 @@ import java.util.Scanner;
* 4 6 * 4 6
* 输出样例: * 输出样例:
* 1 4 * 1 4
* f[i,j]=max(f[i-1,j],f[i-1,j-v[i]]+w[i])
* 其实判断出决策的是哪一项
* 对应图论最短路问题 * 对应图论最短路问题
* f[n-1,m]->f[n,m]边权重为0 * f[n-1,m]->f[n,m]边权重为0
* f[n-1,m-v[i]]+w[i]->f[n,m] 边权重为w[i] * f[n-1,m-v[i]]+w[i]->f[n,m] 边权重为w[i]
* 也就是倒着求,f[n,m]是从哪一条边转移过来的
* 直接判断是f[n-1,m]==f[n,m]还是f[n-1,m-v[i]]+w[i]==f[n,m]
* 如果都相等,说明可选第i号物品,如果只有右边相等说明必选,如果只有左边相等说明不能选
* 就能判断决策
* 要求字典序最小: * 要求字典序最小:
* 可能最大价值有多种方案 * 可能最大价值有多种方案
* 对于一个物品:有三种情况,必选,可选,不能选 * 对于一个物品:有三种情况,必选,可选,不能选
...@@ -47,14 +53,14 @@ public class 背包问题求具体方案 { ...@@ -47,14 +53,14 @@ public class 背包问题求具体方案 {
v[i] = sc.nextInt(); v[i] = sc.nextInt();
w[i] = sc.nextInt(); w[i] = sc.nextInt();
} }
for (int i = n; i >= 1; i--) { for (int i = n; i >= 1; i--) {//倒序枚举每个物品,方便倒推方案
for (int j = 0; j <= m; j++) { for (int j = 0; j <= m; j++) {
f[i][j] = f[i + 1][j]; f[i][j] = f[i + 1][j];
if (j >= v[i]) if (j >= v[i])
f[i][j] = Math.max(f[i][j], f[i + 1][j - v[i]] + w[i]); f[i][j] = Math.max(f[i][j], f[i + 1][j - v[i]] + w[i]);
} }
} }
//f[1][m]是最大值 //f[1][m]是最大值,是结果
int j = m; int j = m;
for (int i = 1; i <= n; i++) {//1~n个物品可选 for (int i = 1; i <= n; i++) {//1~n个物品可选
if (j >= v[i] && f[i][j] == f[i + 1][j - v[i]] + w[i]) { if (j >= v[i] && f[i][j] == f[i + 1][j - v[i]] + w[i]) {
......
package dp.背包模型;
import java.util.Arrays;
import java.util.Scanner;
/**
* 有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
* 第 i 件物品的体积是 vi,价值是 wi。
* 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
* 输出 最优选法的方案数。注意答案可能很大,请输出答案模 10^9+7 的结果。
* 输入格式
* 第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
* 接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
* 输出格式
* 输出一个整数,表示 方案数 模 109+7 的结果。
* 数据范围
* 0<N,V≤1000
* 0<vi,wi≤1000
* 输入样例
* 4 5
* 1 2
* 2 4
* 3 4
* 4 6
* 输出样例:
* 2
* 题目问最大价值的方案数量
* 而不是加满背包的方案数量
* 最短路(0,0)->通过各种路径到(n,m)最短路径的条数
* f[i,j]=max ( f[i-1,j] f[i-1,j-v[i]]+w[i] )
* 最优解,最短路的,起点到最后一条边的方案数累加到终点
* 当然有可能又多条最短路
* g[i,j]代表f[i,j]取最大值的数量
* g[i,j]的计算对于f[i-1,j]的转移就是g[i-1,j]
* 对于f[i-1,j-v]+w的转移g[i-1,j-v]
* 同时转移:g[i-1,j]+g[i-1,j-v]
*/
public class 背包问题求方案数量 {
static int mod = (int) (1e9 + 7);
static int n, m;
static int[] f = new int[1010];
static int[] g = new int[1010];//存的的恰好
static int[] v = new int[1010];
static int[] w = new int[1010];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
Arrays.fill(f, -0x3f3f3f3f);
f[0] = 0;
g[0] = 1;
for (int i = 1; i <= n; i++) {
v[i] = sc.nextInt();
w[i] = sc.nextInt();
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >= v[i]; j--) {
int maxv = Math.max(f[j], f[j - v[i]] + w[i]);
int cnt = 0;
if (maxv == f[j]) cnt += g[j];
if (maxv == f[j - v[i]] + w[i]) cnt += g[j - v[i]];
g[j] = cnt % mod;
f[j] = maxv;
}
}
int res = 0;
for (int i = 0; i <= m; i++) {
res = Math.max(res, f[i]);
}
int cnt = 0;
for (int i = 0; i <= m; i++) {
if (res == f[i]) {
cnt = (cnt + g[i]) % mod;
}
}
System.out.println(cnt);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册