学习范围:背包和二分
背包:
种类:
1.01背包:
这次作业的第一题:
Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …
The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?
每种物品仅有一件,可以选择放或不放。用子问题定义状态:**即F[i;v] 表示前i 件物品恰放入一个容量为v的背包可以获得的最大价值。**则其状态转移方程便是:
F[i;v] = max(F[i-1;v],F[i-1;v-Ci] + Wig)
背包有重量限制,当我们找寻物品时,无法确定物品是否被装入背包,就更难以找到装某一物品时与之前状态的关联。通过自定义的一维或二维数组为我们将物品装入背包这个行为定义成状态的变化,从而找到与上一次装物品之间的关联。
int main() {
int t;
int dp[1005];//dp数组
while (cin >> t) {
while (t--) {
int n, m;
int h;
memset(dp, 0, sizeof(dp));
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> S[i].x;
}
for (int i = 1; i <= n; i++) {
cin >> S[i].y;
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >= S[i].y; j--) {
dp[j] = max(dp[j], (dp[j - S[i].y]) + S[i].x);
}
}
cout << dp[m] << endl;
}
}
}
2.完全背包
每种物品都有无限件可用。为01背包的变形。但同时可以变形为01背包。
f[i][j]:表示前i种物品放入一个容量为j的
f[i][j]只与f[i-1][j]和f[i][j-w[i]]+v[i]有关。
考虑f[i][j]时,由于是从前往后写,一维数组表示的f[j]还没被写入,它表示的是f[i-1][j],而f[j-w[i]]已经被写入,它表示的是f[i][j-w[i]]
一维的
f[v]=max{f[v],f[v-w[i]]+v[i]}
表示的恰好是二维的
f[i][v]=max{f[i-1][v],f[i][v-w[i]]+v[i]}
课件上的例题:
题意:有n种食物。每种食物含有的热量和带来的幸福感分别为a[i]和b[i]。一个人每天
摄入的热量不超过m。
求在不超过一天热量摄入量前提下,可获得的最多幸福感是多少。
代码:
int main()
{
int t;
while(~scanf("%d",&n))
{
memset(dp,0,sizeof(dp));
int i,j,m;
int a[105],b[105];
for(i = 0; i<t; i++)
scanf("%d%d",&a[i],&b[i]);
scanf("%d",&m);
for(i = 0; i<n; i++)
{
for(j = a[i]; j<=m; j++)
{
dp[j] = max(dp[j],dp[j-a[i]]+b[i]);
}
}
3.分组背包
首先判断一个分组当中的一件物品,同01背包一样,此物品存在两种状态,取与不取,若取此物品,则继续判断下一组的第一件物品,若不取此物品,则继续判断本组下一件物品,若该物品为本组最后一件物品,则判断下一组。也就是说设f[k][v]表示前k组物品花费费用v能取得的最大权值
状态方程:
f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]}
题:
给定n门课,再用m天复习这n门课,然后给定一个n*m的矩阵A,A[i][j]表示用j天复习第i门课获得得收益。问用m天复习不同的课获得的最大收益。
这题可以转化成将天数m作为背包的容量,科目数目作为背包的种类数目,天数j作为背包的重量,因为一个科目只能选一次(复习天数),对应于每组中的物品只能选一件,正好是分组背包问题。
int main()
{
int i,j,k,n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0)
break;
memset(dp,0,sizeof(dp));
for(i=0;i<n;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for(i=0;i<n;i++)
for(j=m;j>=0;j--)
for(k=1;k<=j;k++)
dp[j]=max(dp[j],dp[j-k]+a[i][k]);
printf("%d\n",dp[m]);
}
4.二分
之前有接触过。
在一个单调有序的集合中查找元素,每次将集合分为左右两部分,判断解在哪个部分中并调整集合上下界,重复直到找到目标元素。
注意要是单调,不是单调的话要分成几个单调的部分找。
while(high - low > 1.0e-9)
{
mid = (high + low)/2;
if(Caculate(mid)<x)
low=mid;
else
high=mid;
}
注意:不要直接写大于0,容易出问题,写成1.0e-9即可