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

c

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