题目 | 类型 |
---|---|
混合背包问题 | 混合背包问题 |
机器分配 | 背包问题方案数量 |
背包问题求方案数 | 背包问题方案数量 |
1、混合背包问题
有 N 种物品和一个容量是 V 的背包。
物品一共有三类:
第一类物品只能用1次(01背包); 第二类物品可以用无限次(完全背包); 第三类物品最多只能用 si 次(多重背包); 每种体积是 vi ,价值是 wi 。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。 输出最大价值。
输入格式
第一行两个整数,N,V ,用空格隔开,分别表示物品种数和背包容积。接下来有 N 行,每行三个整数 vi,wi,si ,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
si=−1 表示第 i 种物品只能用1次; si=0 表示第 i 种物品可以用无限次; si>0 表示第 i 种物品可以使用 si 次;
输出格式
输出一个整数,表示最大价值。数据范围
0<N,V≤1000
0<vi,wi≤1000
−1≤si≤1000
输入样例
4 5
1 2 -1
2 4 1
3 4 0
4 5 2
输出样例:
8
思路:
完全背包问题可以转化为多重背包问题,这是本题的关键所在,完全背包能用无数次,等价于可以一种物品用到背包满为止,所以我们有/转化公式:
s[i]=m/v[i];
将完全背包转化为多重背包
另外,本题采用二进制优化来加快程序运行速度,二进制优化打包后就相当于01背包问题
代码:
#include<bits/stdc++.h>using namespace std;const int N=100010;int v[N],w[N],s[N];
int chmodv[N];
int chmodw[N];
int f[N];//将完全背包转化为多重背包(能取无数次相当于有背包体积/v个物品)
//-1 01 0 完全 >0 多重int n,m;//m是背包容积
int main()
{cin>>n>>m;for(int i=1;i<=n;i++){cin>>v[i]>>w[i]>>s[i];if(s[i]==-1)s[i]=1;//01背包转化为多重背包else if(s[i]==0)s[i]=m/v[i];//完全背包转化为多重背包}//二进制优化(朴素写法要三重循环,在第三重循环枚举s[i])//二进制优化后,多个物品被打包,相当于01背包问题//所以只需要01背包的做法(二重循环)int cnt=1;for(int i=1;i<=n;i++){int k=1;//用来二进制打包while(k<=s[i]){chmodv[cnt]=v[i]*k;chmodw[cnt]=w[i]*k;s[i]-=k;k*=2;cnt++;}if(s[i]>0){chmodv[cnt]=s[i]*v[i];chmodw[cnt]=s[i]*w[i];cnt++;}}///二进制优化完成// 朴素写法
// for(int i=1;i<=cnt;i++)//这时候是多个物品打包在一起,做法和01背包一样
// {
// for(int j=0;j<=m;j++)
// {
// f[i][j]=f[i-1][j];
// if(j>=chmodv[i])f[i][j]=max(f[i][j],f[i-1][j-chmodv[i]]+chmodw[i]);
// }
//
// }// 滚动数组写法(可以储存更多的状态,如果是二维存储的状态不够用)for(int i=1;i<=cnt;i++){for(int j=m;j>=chmodv[i];j--){f[j]=max(f[j],f[j-chmodv[i]]+chmodw[i]);}}cout<<f[m];return 0;
}
2、机器分配
总公司拥有 M 台 相同 的高效设备,准备分给下属的 N 个分公司。
各分公司若获得这些设备,可以为国家提供一定的盈利。盈利与分配的设备数量有关。
问:如何分配这M台设备才能使国家得到的盈利最大?
求出最大盈利值。
分配原则:每个公司有权获得任意数目的设备,但总台数不超过设备数 M 。
输入格式
第一行有两个数,第一个数是分公司数 N ,第二个数是设备台数 M ;接下来是一个 N×M 的矩阵,矩阵中的第 i 行第 j 列的整数表示第 i 个公司分配 j 台机器时的盈利。
输出格式 第一行输出最大盈利值;
接下 N 行,每行有 2 个数,即分公司编号和该分公司获得设备台数。
答案不唯一,输出任意合法方案即可。
数据范围
1≤N≤10 , 1≤M≤15
输入样例:
3 3
30 40 50
20 30 50
20 25 30
输出样例:
70 1 1
2 1 3 1
思路:
每一个公司可以看作一个组,从每一组中选
最终用dfs寻找路径:!!从最终状态开始dfs!!倒推到初始状态
代码:
#include<bits/stdc++.h>using namespace std;const int N=12,M=17;int n,m;//公司数量和机器数量int g[N][M];int f[N][M];int path[N];
int cnt;void dfs(int i,int j)
{if(i==0)return ;for(int a=0;a<=j;a++){if(f[i-1][j-a]+g[i][a]==f[i][j]){path[cnt++]=a;dfs(i-1,j-a);return ;}}}int main()
{cin>>n>>m;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){scanf("%d",&g[i][j]);//读入数据矩阵}//每个公司可以看作一个组,从每一组中选for(int i=1;i<=n;i++)//从前i组里面选{for(int j=1;j<=m;j++)//枚举体积(总共m台机器){for(int k = 0;k <= j;k ++){f[i][j]=max(f[i][j],f[i-1][j]);f[i][j]=max(f[i][j],f[i-1][j-k]+g[i][k]);//g[i][k]可以解读为第i组中体积为k,价值为g[i][k]}}}cout<<f[n][m]<<endl;//find pathdfs(n,m);//因为是从最终状态开始find拓扑排序的//所以cnt==0的时候存储的是最终的状态//最终path要倒序输出for(int i=cnt-1,id=1;i>=0;id++,i--)cout<<id<<" "<<path[i]<<endl;
}
3、背包问题求方案数
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi ,价值是 wi 。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出 最优选法的方案数。注意答案可能很大,请输出答案模 109+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
思路:
cnt[i]存储价值为i的时候的方案数
相当于进行两次dp
代码:
#include<bits/stdc++.h>using namespace std;//01背包问题求方案数
//f[i]存储体积为i的时候的最高价值
//cnt[i]存储体积为i的时候的最高价值(f[i])对应的方案数const int N=1003;int f[N];
int cnt[N];int v[N],w[N];int mod=1e9+7;int main()
{int n,m;cin>>n>>m;for(int i = 0; i <= m; i ++) cnt[i] = 1;//初始值全部设置为1//因为最高价值都可以为0,方案就可以有一种for(int i=1;i<=n;i++){scanf("%d%d",&v[i],&w[i]);}for(int i=1;i<=n;i++)for(int j=m;j>=v[i];j--){int value=f[j-v[i]]+w[i];if(value>f[j])//找到价值更高的方案{f[j]=value;cnt[j]=cnt[j-v[i]];}else if(value==f[j]){cnt[j]=(cnt[j]+cnt[j-v[i]])%mod;}}cout<<cnt[m];return 0;
}