LGJOI20230812

发布时间 2023-08-12 15:47:11作者: AzusidNya

LGJ水场。

这场总体题比较简单,所以分比较高。


A

\(n\) 项工作,完成一项工作需要 \(1\) 单位时间。每项工作有个截止时间 \(t\) 和报酬 \(v\),需要在第 \(t\) 单位时间前完成工作才能得到 \(v\) 的报酬。给定 \(T\),求 \(T\) 时间后获得报酬的最大值。

solution:

简单贪心。将工作按截止日期从小到大排序。用一个堆维护当前决定进行的工作的报酬,每次尝试加入一项工作,如果超过了截止时间就从堆中取出报酬最小的工作,如果其报酬小于当前工作的报酬就将其移出堆并让当前工作入堆并更新报酬。

code

#include<iostream>
#include<fstream>
#include<algorithm>
#include<queue>
#define int long long
using namespace std;
int T, n;
struct node_t{
	int val, r;
}a[2000005];
bool cmp(node_t u, node_t v){
	return u.r == v.r ? u.val > v.val : u.r < v.r;
}
priority_queue<int> que;
int ans, cnt;
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> T >> n;
	for(int i = 1; i <= n; i ++)
		cin >> a[i].r >> a[i].val, a[i].r = min(a[i].r, T);
	sort(a + 1, a + n + 1, cmp);
	for(int i = 1; i <= n; i ++){
		 if(cnt + 1 <= a[i].r){
		 	ans += a[i].val;
		 	que.push(-a[i].val);
		 	cnt ++;
		 }
		 else{
		 	int last = -que.top();
		 	if(a[i].val < last) continue;
		 	que.pop();
		 	ans -= last, ans += a[i].val;
		 	que.push(-a[i].val);
		 }
	}
	cout << ans;
	return 0;
}

B

\(n\) 种货币,第 \(i\) 种货币有 \(c_i\) 张,价值为 \(v_i\)。现在要购买价值为 \(m\) 的物品。店铺设找零,每种货币都有无数张。希望能够找出一种方案使购买使用的货币数与找零使用的货币数的和最小,求出这个最小值。

solution

假设对于第 \(i\) 种货币找零的价值为 \(-v_i\),那可以转化为一个多重背包问题加上一个完全背包问题。单调队列优化多重背包,然后再完全背包即可。有一个要注意的点是背包时背包容量上限要尽量开大点以免被卡。

code

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cstring>
#pragma GCC optmize(3,"Ofast","inline")
#define int long long
using namespace std;
int n, M, N, tot;
int w[405], c[405];
int q1[405], q2[405];
int cnt[205];
const int inf = 0x3f3f3f3f3f3f3f3f;
int f[2][300005], nw;
int q[300005], head = 1, tail = 0;
int sum;
signed main(){
	ios::sync_with_stdio(0);
	cin >> n >> N;
	for(int i = 1; i <= n; i ++)
		cin >> q1[i];
	for(int i = 1; i <= n; i ++)
		cin >> q2[i], sum += q1[i] * q2[i];
	for(int i = 1; i <= n; i ++)
		cnt[q1[i]] += q2[i];
	for(int i = 1; i <= 200; i ++)
		if(cnt[i]) w[++ tot] = i, c[tot] = cnt[i];
	for(int i = tot + 1; i <= tot * 2; i ++)
		w[i] = -w[i - tot], c[i] = inf;
	n = tot;
	memset(f, 0x3f, sizeof(f));
	f[0][0] = 0;
	M = N * 2;
	for(int i = 1; i <= n; i ++){
		nw = nw ^ 1;
		for(int j = 0; j < w[i]; j ++){
			head = 1, tail = 1, q[1] = 0;
			f[nw][j] = f[nw ^ 1][j];
			for(int k = 1; k <= (M - j) / w[i]; k ++){
				while(head <= tail && k - q[head] > c[i])
					head ++;
				while(head <= tail && f[nw ^ 1][j + k * w[i]] <= f[nw ^ 1][j + q[tail] * w[i]] + (k - q[tail]))
					tail --;
				q[++ tail] = k;
				f[nw][j + k * w[i]] = f[nw ^ 1][j + q[head] * w[i]] + (k - q[head]);
			} 
		}
	}
	for(int i = n + 1; i <= 2 * n; i ++){
		for(int j = M; j >= 1; j --){
			if(j - w[i] > M) continue;
			f[nw][j] = min(f[nw][j], f[nw][j - w[i]] + 1);
		}
	}
	if(f[nw][N] < inf)
		cout << f[nw][N];
	else
		cout << -1;
	return 0;
}