Problem J: 黄金矿工
Time Limit: 1 Sec Memory Limit: 128 MB
Submit: 81 Solved: 39
[Submit][Status][Web Board]
Description
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
题目大意:
对于同一条直线上的金子,必须把前面的拿走才能拿后面的。举个例子,有 1,2,3,三块金子,有拿1,拿 1 2,拿1 2 3三种拿法,同时他们之间是互斥的,三种拿法中只能取一种拿法。这样就变成了分组背包问题。
思路:
变成分组背包问题后,接下来就是怎么分组了,很明显按直线的斜率分组,同一直线上的物品为同一组,接下来就是套模板了
我在代码里注释了。
AC代码:
#include<iostream>//分组背包
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
const int MAX = 500;
using namespace std;
struct node
{
double k;
int x, y, t, v;
}gold[MAX];//存物品
bool cmp(node x, node y)//同一直线上的排在一起
{
if (x.k = y.k)
return x.y<y.y;
return x.k<y.k;
}
int f[40005], t[40005];//f数组存最大价值,t数组用来标记哪些是同一组的
int main()
{
int n, T;
while (scanf("%d %d", &n, &T) != EOF)
{
memset(f, 0, sizeof(f));//初始化数组
memset(t, 0, sizeof(t));
for (int i = 0; i<n; i++)
{
scanf("%d %d %d %d", &gold[i].x, &gold[i].y, &gold[i].t, &gold[i].v);
gold[i].k = gold[i].x*1.0 / gold[i].y;//计算斜率,判断是否在同一条直线上。
}
sort(gold, gold + n, cmp);//排序
int flag = 0;
for (int i = 0; i<n; i++)//标记哪些物品是同一组的
{
if (gold[i].k == gold[i - 1].k)
t[i] = flag;
else
t[i] = ++flag;
}
int p = 0, q;
/*多重背包模板:
for 所有的组k
for v=V..0
for 所有的i属于组k
f[v]=max{f[v],f[v-c[i]]+w[i]}
*/
for (int i = 1; i <= flag; i++)//总有flag个组
{
for (int j = T; j>0; j--)//容积逆序循坏
{
int time = 0, v = 0;
for (int z = p; z<n; z++)//循环组内的物品
{
time += gold[z].t;
v += gold[z].v;
if (j >= time)//j小于time就不用更新了。
f[j] = max(f[j], f[j - time] + v);
// cout<<j<<" "<<f[j]<<" "<<z<<endl;
if (t[z + 1] != t[z])//这一组的物品循环完了
{
q = z + 1;//记录下一组开始的位置
break;
}
}
}
p = q;//更新开始的位置
}
cout << f[T] << endl;
}
return 0;
}