zcmu 2165: 黄金矿工

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

TJOI2013

题目分析

              很明显这是一个分组背包的问题,因为每条直线上可能有不止一块金块,得到最优解可能是拿一块可能拿两块,

 但是不可能同时拿一块和两块,这就符合了分组背包的条件

分组背包

有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;
}

猜你喜欢

转载自blog.csdn.net/weixin_42105529/article/details/81352589