T3:最大子集

本题是 01背包 的变种题

dp[i] 表示选到的奶牛的智商总和为 \(i\) 时对应的情商总和的最大值

这里由于 \(x\) 可能是负数,所以需要将 \(i\) 向后偏移 \(3e5\)

特别地,在转移时,当 \(x>0\) 时,倒序枚举 \(i\);而当 \(x < 0\) 时,则需要正序枚举 \(i\)
理由也很简单,是为了保证 \(dp\) 转移的无后效性

原题:[USACO03FALL]Cow Exhibition G

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

inline void chmax(int& x, int y) { if (x < y) x = y; }

int dp[600005];

int main() {
    int n;
    cin >> n;
    
    memset(dp, 0xcf, sizeof dp);
    
    const int S = 300000, M = S*2;
    dp[S] = 0;
    rep(i, n) {
        int x, y;
        cin >> x >> y;
        
        if (x >= 0) {
            for (int j = M; j >= x; --j) {
                chmax(dp[j], dp[j-x]+y);
            }
        }
        else {
            for (int j = 0; j-x <= M; ++j) {
                chmax(dp[j], dp[j-x]+y);
            }
        }
    }
    
    int ans = 0;
    for (int i = S; i <= M; ++i) {
        if (dp[i] >= 0) {
            chmax(ans, i+dp[i]-S);
        }
    }
    
    cout << ans << '\n';
    
    return 0;
}

T4:接单

可以考虑反悔贪心:

  • 先将每个工作按时间大小排好序,优先做截止时间靠前的工作
  • 用小根堆 \(q\) 维护所选择的工作
  • 扫描每个工作,若当前工作已经超过截止时间的话,那么为了让报酬最大化,(注意到每个时间点只能做一个工作),若当前的工作利润比前面选的工作中报酬最小的更高的话,那么就可以用当前的工作替换掉;否则直接选即可

原题:[USACO09OPEN]Work Scheduling G

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using P = pair<int, int>;
using ll = long long;

int main() {
	int n;
	cin >> n;
	
	vector<P> p(n);
	rep(i, n) cin >> p[i].first >> p[i].second;
	
	sort(p.begin(), p.end());
	
	ll ans = 0;
	priority_queue<int, vector<int>, greater<int>> q;
	for (auto [d, v] : p) {
		if (d <= q.size()) {
			int u = q.top(); q.pop();
			if (v > u) ans += v-u;
		}
		else ans += v;
		q.push(v);
	}
	
	cout << ans << '\n';
	
	return 0;
}