Gold miner HDU - 4341(分组背包)

题意:
黄金矿工,在时间T内,在给定的矿石中获得其中部分矿石的最大价值和。
如果矿石在同一条线上,要先把线上的前面的矿石拿走,才可以接着拿后面的矿石。

题解:
把每个矿石的斜率求出来,当斜率相同,就是在同一条直线上了。因为横坐标可能为0.所以求的是斜率的倒数。
对于数据中的每个斜率,就为一组物品。每组物品要么取一个,要么取两个。。。。

#pragma GCC optimize(2,"Ofast","inline")
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<vector>
#include<cstring>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
#include<cstdlib>
#include<bitset>
#include<functional>
#include<ctime>
#define F(i,s,t) for(int i=(s);i<=(t);i++)
#define D(i,s,t) for(int i=(s);i>=(t);i--)
#define dBug(i) printf("Value=%d\n",i)
#define ddBug(i,j) printf("Value=%d %d\n",i,j)
#define ed putchar('\n')
#define FO freopen("D:\\in.txt","r",stdin)
#define IOS cin.tie(0) ,cout.tie(0), cout.sync_with_stdio(0)
typedef long long ll;
//const int INF = 1 << 30;
//const double EPS = 1e-6;
//#define MX 1002
//#define Mod 10000
using namespace std;

struct Mineral
{
	int x, y, t, v;
};
int N, T;
double t1, t2;//t1表示a的斜率
Mineral M[210];
vector<Mineral> G[210];//用于存第i组的矿石
int dp[40010];

bool cmp(Mineral &a, Mineral &b)//按斜率的倒数降序排序
{
	t1 = (double)a.x / a.y;
	t2 = (double)b.x / b.y;
	if (t1 != t2)//当斜率不同时
		return t1 < t2;

	else{ // 当a和b 矿山在同一直线上
		t1 = a.x * a.x + a.y * a.y;  //按离原点的远近升序排序
		t2 = b.x * b.x + b.y * b.y;
		return t1 < t2;
	}
}

int main()
{
	IOS;
	//FO;
	int cas(0);//用于记录第几组数据
	while (scanf("%d %d", &N, &T) != EOF){
		memset(dp, 0, sizeof(dp));//初始化
		F(i, 1, N)	G[i].clear();//初始化

		F(i, 1, N)
			scanf("%d %d %d %d", &M[i].x, &M[i].y, &M[i].t, &M[i].v);

		sort(M + 1, M + N + 1, cmp);//对矿石进行排序
		/*cout << endl; ed;
		F(i, 1, N)
			cout << M[i].x << " " << M[i].y << " "
			<< M[i].t << " " << M[i].v << endl;
		ed; ed;*/
		//下面进行矿石分组
		int Group(1);//用于存组数,初始值为1,因为从2号矿石开始的
		G[Group].push_back(M[1]);
		t1 = (double)M[1].x / M[1].y; //在这里t1表示上一号矿石的斜率
		F(i, 2, N){
			t2 = (double)M[i].x / M[i].y;
			if (t1 != t2){ //当矿石不在同一直线上
				Group++;
				G[Group].push_back(M[i]);
			}//if
			else{//当矿石在同一直线上,组号不用加1了,用上次的组号就好
				M[i].t += M[i - 1].t;//要改一下时间和价值
				M[i].v += M[i - 1].v;
				G[Group].push_back(M[i]);
			}
			t1 = t2;
		}//for

		/*cout << endl; ed;
		F(i, 1, Group){
			cout << i << "组"; ed;
			F(j, 0, G[i].size() - 1){
				cout << G[i][j].x << " " << G[i][j].y << " "
					<< G[i][j].t << " " << G[i][j].v << endl;
			}
		}
		ed; ed;*/

		//下面是分组背包了
		F(i, 1, Group)//枚举分组
			D(j, T, 1)//枚举时间
			F(k, 0, G[i].size() - 1){ //枚举组里面每一个价值
				if (j >= G[i][k].t)//背包可以装第i组的第k个物品
					dp[j] = max(dp[j], dp[j - G[i][k].t] + G[i][k].v);
			}//inner for
		printf("Case %d: %d\n", ++cas, dp[T]);
	}//while
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值