Input
3 10
1 1 1 1
2 2 2 2
1 3 15 9
Output
3
Sample Input
Sample Output
HINT
-----sample2------
1 1 13 1
2 2 2 2
1 3 4 7
-----sample2-----
7
-----------
30%的数据,0 < T ≤ 4000
100%的数据,N ≤ 200, 0 < T ≤ 40000
Source
题目分析
很明显这是一个分组背包的问题,因为每条直线上可能有不止一块金块,得到最优解可能是拿一块可能拿两块,
但是不可能同时拿一块和两块,这就符合了分组背包的条件
分组背包
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
算法
这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。也就是说设f[k][v]表示前k组物品花费费用v能取得的最大权值,则有:
f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于第k组}
使用一维数组的伪代码如下:
for 所有的组k
for v=V..0
for 所有的i属于组k
f[v]=max{f[v],f[v-c[i]]+w[i]}
注意这里的三层循环的顺序,甚至在本文的beta版中我自己都写错了。“for v=V..0”这一层循环必须在“for 所有的i属于组k”之外。这样才能保证每一组内的物品最多只有一个会被添加到背包中。
另外,显然可以对每组内的物品应用P02中“一个简单有效的优化”。
小结
分组的背包问题将彼此互斥的若干物品称为一个组,这建立了一个很好的模型。不少背包问题的变形都可以转化为分组的背包问题(例如P07),由分组的背包问题进一步可定义“泛化物品”的概念,十分有利于解题。
这是背包九讲里对分组背包的描述
假如一条直线上有n个金块,我们如果要取则有n种取法,我们将这n种取法当成一种组合
这就是一个典型分组背包
AC一
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct node
{
double jd;
int w;
int v;
int dis;
}k[1000];
bool cmp(node a, node b)
{
if (a.jd == b.jd)
return a.dis < b.dis;
else
return a.jd < b.jd;
}
int mk[1000];
int dp[40010];
int main()
{
int n, t;
while (cin >> n >> t)
{
memset(mk, 0, sizeof(mk));
memset(k, 0, sizeof(k));
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++)
{
int x, y,w,v;
cin >> x >> y >> w >>v;
k[i].w = w;
k[i].v = v;
k[i].jd = (double)(x) / (double)y;
k[i].dis = x * x + y * y;
}
sort(k+1, k + n+1, cmp);
int xb = 1;
mk[1] = 1;//学到了,学到了,判断集合原来还能这么玩
for (int i = 2; i <= n; i++)
{
if (k[i].jd == k[i - 1].jd)
{
mk[i] = xb;
k[i].v += k[i - 1].v;//累加之前相同角度的金矿作为整体进行考虑
k[i].w += k[i - 1].w;
}
else
{
mk[i] = ++xb;
}
}
int i, j, z,zs=1;//zs代表每组金矿的起始下标
for (i = 1; i <= xb; i++)//总的金矿组数
{
for (j =t; j>=1; j--)//背包当前容量
{
z = zs;
for (; mk[z] == i;z++)//每组金矿中选一个(或者一个都不选)
{
if (j >= k[z].w)
{
dp[j] = max(dp[j], dp[j - k[z].w] + k[z].v);
}
}
}
zs = z;
}
/*for (int i = 1; i <= t; i++)
cout << dp[i] << " ";
cout << endl;*/
cout << dp[t]<< endl;
}
return 0;
}
AC二
#include <bits/stdc++.h>
using namespace std;
struct node
{
double jd;
int t,v,x;
}gold[40000];
int dp[40010];
int N,T;
bool cmp(node x,node y)
{
if(x.jd==y.jd)
return x.x<y.x;
else
return x.jd<y.jd;
}
int main()
{
while(cin>>N>>T)
{
for(int i=0;i<N;i++)
{
int x,y,t,v;
cin>>x>>y>>t>>v;
gold[i].t=t;
gold[i].v=v;
gold[i].jd=y*1.0/x;
gold[i].x=hypot(x,y);//点到原点的距离
}
sort(gold,gold+N,cmp); //分组前的准备工作
int t=0;
for(int i=0;i<N;i++)
{
gold[i].jd==gold[i-1].jd?t+=gold[i].t:t=gold[i].t;
for(int j=T;j>=t;j--)
dp[j]=max(dp[j],dp[j-gold[i].t]+gold[i].v);
}
cout<<dp[T]<<endl;
}
return 0;
}