题解P3961 [TJOI2013] 黄金矿工

题目传送门https://www.luogu.com.cn/problem/P3961

题目分析

由于小A不能移动,想挖掘一些矿石要求已经挖掘另一些,注意到这些相互依赖的矿石组内有一个特点,即与原点连线的斜率是一定的,因此,我们把斜率为 k 的点都放到集合 A_k 中。然后考虑答案的更新方式,最优答案考虑了所有的 cnt 个集合,假设其由前 cnt-1 个集合更新而来,由于挖掘矿石耗费时间,不同时间下的最优方案表现不同,所以我们要考虑在花费时间为 t 的情况下,考虑前 pos 个集合所能达到的最大收益,这样一来转移方程式就呼之欲出了:

                                     ​​​​f[i][j]=max(f[i-1][j-cost_{n,q}]+val_{n,q})

注意到由于斜率不是整数,我们需要将每一个斜率 k 映射到一个整数 n,并分别考虑挖掘当前集合内前 q 个矿石

代码

#include <iostream>
#include <cmath>
using namespace std;
int f[201][40001];
int shot[201][201];
int cnt = 0;
int n, T;
int gcd(int a, int b) {
	int r = 0;
	if (a < b)
		swap(a, b);
	while (b) {
		r = a % b;
		a = b;
		b = r;
	}
	return a;
}
struct node {
	int val, cost;
};
node Ore[201][201];//Ore[i][j]表示在挖取基底映射为i的前j个矿石下,所需要的成本与收益
int num[201];//num[i]表示基底映射为i的矿石数量
void Update(int pos, int W, int V) {
	num[pos]++;
	Ore[pos][num[pos]] = Ore[pos][num[pos] - 1];
	Ore[pos][num[pos]].val += V;
	Ore[pos][num[pos]].cost += W;
	return;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> T;
	int x, y, t, v,x_b,y_b;
	for (int i = 1;i <= n;i++) {
		cin >> x >> y >> t >> v;
		int k = gcd(x, y);
		x_b = abs(x / k), y_b = abs(y / k);
		if (!shot[x_b][y_b]) {
			cnt++;
			shot[x_b][y_b] = cnt;
		}
		Update(shot[x_b][y_b], t, v);
	}
	for (int i = 1;i <= cnt;i++) {
		for (int j = 0;j <= T;j++) {
			for (int q = 0;q <= num[i];q++) {
				if (j < Ore[i][q].cost)
					break;
				f[i][j] = max(f[i][j], f[i - 1][j - Ore[i][q].cost] + Ore[i][q].val);
			}
		}
	}
	cout << f[cnt][T];
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值