一、机器分配问题
1.问题引入:
总公司拥有高效生产设备N台,准备分给下属的M个公司。各分公司若获得这些设备,可以为国家提供一定的盈利。问:如何分配这M台设备才能使国家得到的盈利最大?求出最大盈利值。其中N《=15,M〈=10。分配原则:每个公司有权获得任意数目的设备,但总台数不得超过总设备数N。
第一行输入两个数,第一个数是分公司数M,第二个数是设备台数N。接下来是一个M*N的矩阵,表明了第I个公司分配J台机器的盈利。
最后输出最大盈利
样例输入:
3 3 30 40 50 20 30 50 20 25 30
样例输出:
70
2.思路分析:
动态规划是由递归演变而来的,就是记录递归的中间过程,所以动态规划牺牲空间来优化了时间,其实方法还是递归搜索。让公司个数为状态,一个是阶段另一个肯定是状态了。我们可以把状态设置为个数这样我们就能考虑到因为个数不同而价值不同计算了 , 我们把公司设置为阶段。我们设置数组为d[i][j]表示前i个公司用j个机器。我们用 d[i][j]来记录到i阶段的j台机器时价值的最大值。
如何得到d[i][j]呢 , 我们可以从d[i-1]行中找,如果遍历到第i-1个公司已经用了K台机器,则第i个公司可以用j-k(k<=j)台机器。由此得到状态转移方程为d[i][j]=Max{d[i-1][k]+value[i][j-k]}。
3.代码如下:
package 动态规划资源问题 ;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class 机器分配问题{
public static void main(String args[]) throws IOException{
BufferedReader buf=new BufferedReader(new InputStreamReader(System.in));
String s=buf.readLine();
String s1[]=s.split(" ");
int m=Integer.parseInt(s1[0]);//公司
int n=Integer.parseInt(s1[1]);
int v[][]=new int[m+1][n+1]; //有效下标从1开始
for(int i=1;i<=m;i++){
String t=buf.readLine();
String t1[]=t.split(" ");
for(int j=1;j<=n;j++)
v[i][j]=Integer.parseInt(t1[j-1]);
}
int max=fun(v,m,n);
System.out.print(max);
}
public static int fun(int v[][],int m,int n){
int dp[][]=new int[m+1][n+1];//有效下标从1开始
for(int i=0;i<=m;i++) dp[i][0]=0;
for(int i=0;i<=n;i++) dp[0][i]=0;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
for(int k=0;k<=j;k++)
dp[i][j]=Math.max(dp[i][j],
dp[i-1][k]+v[i][j-k]);//前i个公司用了k台机器,则第i个公司可以用j-k台机器
}
}
return dp[m][n];
}
}
ackage 动态规划资源问题 ;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class 机器分配问题{
public static void main(String args[]) throws IOException{
BufferedReader buf=new BufferedReader(new InputStreamReader(System.in));
String s=buf.readLine();
String s1[]=s.split(" ");
int m=Integer.parseInt(s1[0]);//公司
int n=Integer.parseInt(s1[1]);
int v[][]=new int[m+1][n+1]; //有效下标从1开始
for(int i=1;i<=m;i++){
String t=buf.readLine();
String t1[]=t.split(" ");
for(int j=1;j<=n;j++)
v[i][j]=Integer.parseInt(t1[j-1]);
}
int max=fun(v,m,n);
System.out.print(max);
}
public static int fun(int v[][],int m,int n){
int dp[][]=new int[m+1][n+1];//有效下标从1开始
for(int i=0;i<=m;i++) dp[i][0]=0;
for(int i=0;i<=n;i++) dp[0][i]=0;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
for(int k=0;k<=j;k++)
dp[i][j]=Math.max(dp[i][j],
dp[i-1][k]+v[i][j-k]);//前i个公司用了k台机器,则第i个公司可以用j-k台机器
}
}
return dp[m][n];
}
}
二、01背包问题
1.问题引入:
name | weight | value | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
a | 2 | 6 | 0 | 6 | 6 | 9 | 9 | 12 | 12 | 15 | 15 | 15 |
b | 2 | 3 | 0 | 3 | 3 | 6 | 6 | 9 | 9 | 9 | 10 | 11 |
c | 6 | 5 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 10 | 11 |
d | 5 | 4 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 10 | 10 |
e | 4 | 6 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
通过找规律手工填写出上面这张表就能理解背包的动态规划算法了。状态转移方程为:
f[i,j] = Max{ f[i-1,j-Wi]+Pi( j >= Wi ),//表示第i件物品放入
f[i-1,j] //表示第i件物品不放入}
首先要明确这张表是至底向上,从左到右生成的。
为了叙述方便,用e2单元格表示e行2列的单元格,这个单元格的意义是用来表示只有物品e时,有个承重为2的背包,那么这个背包的最大价值是0,因为e物品的重量是4,背包装不了。
对于d2单元格,表示只有物品e,d时,承重为2的背包,所能装入的最大价值,仍然是0,因为物品e,d都不是这个背包能装的。
同理,c2=0,b2=3,a2=6。
对于承重为8的背包,a8=15,是怎么得出的呢?
在这里,
f[i-1,j]表示我有一个承重为8的背包,当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值
f[i-1,j-Wi]表示我有一个承重为6的背包(等于当前背包承重减去物品a的重量),当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值
f[i-1,j-Wi]就是指单元格b6,值为9,Pi指的是a物品的价值,即6
由于f[i-1,j-Wi]+Pi = 9 + 6 = 15 大于f[i-1,j] = 9,所以物品a应该放入承重为8的背包
3.代码如下:- <span style="color:#330099;">package 动态规划资源问题;
- public class 01背包问题 {
- public static void main(String args[]){
- int v[]={6,3,5,4,6};
- int w[]={2,2,6,5,4};
- int c=10;
- int max=fun(v,w,5,c);
- System.out.println(max);
- }
- public static int fun(int v[],int w[],int i,int wi){
- int dp[][]=new int[i+1][wi+1];//一维可从0取到i,二维可从0取到wi
- for(int x=0;x<=i;x++)//无重量,不管有多少件物品,价值都是0
- dp[x][0]=0;
- for(int x=0;x<=wi;x++)//没有物品,不管重量多少,价值都是0
- dp[0][x]=0;
- for(int x=1;x<=i;x++){
- for(int y=1;y<=wi;y++){
- if(y>=w[x-1]){
- dp[x][y]=Math.max(dp[x-1][y], v[x-1]+dp[x-1][y-w[x-1]]);
- }else{
- dp[x][y]=dp[x-1][y];
- }
- }
- }
- return dp[i][wi];
- }
- }</span>
三、完全背包问题
1.问题引入
有N种物品和一个重量为V的背包,每种物品都有无限件可用。第i种物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包重量,且价值总和最大。
2.思路分析
类似于01背包问题的解法,01背包问题设二维数组是因为每种物品只有一个,所以i从第一个物品开始往后遍历,而现在物品的个数是无限的,所以运用动态规划的思想,相关变量就是背包的重量了,我们可以设一个一维数组dp[i]表示背包重量为i时得到的最大价值。当背包重量为i时,从第一个物品开始往后遍历,第j个物品最多可以取 i/w[j] 个,那么dp[i]就等于Max { dp[i-k*w[j]]+k*v[j]) }。
3.代码如下
<span style="color:#330099">package 动态规划资源问题;
public class 完全背包问题 {
public static void main(String args[]){
int v[]={6,3,5,4,6};
int w[]={2,2,6,5,4};
int c=10;
int max= fun(v,w,5,c);
System.out.println(max);
}
public static int fun(int v[],int w[],int n,int c){
int dp[]=new int[c+1];
dp[0]=0;//背包无重量
for(int i=1;i<=c;i++){
for(int j=0;j<n;j++){//对每种物品遍历
int m=i/w[j]; //该种物品最多可以放m个
for(int k=0;k<=m;k++){
if(i>=w[j]*k)//还能装
dp[i]=Math.max(dp[i],
dp[i-k*w[j]]+k*v[j]);
}
}
System.out.println(i+" "+dp[i]);
}
return dp[c];
}
}</span>