【LOJ3158】「NOI2019」序列

【题目链接】

  • 点击打开链接

【思路要点】

  • 笔者本题的做法是基于对决策点进行打表得到的,并不能给出严格的证明。但确实得到了一个与标准解法,或是模拟费用流解法完全不同的做法。
  • 考虑一个 O ( N 3 ) O(N^3) O(N3) 的动态规划,将数组按照 a i a_i ai 排序,那么一旦确定了 L L L 对都选的位置,剩余的 K − L K-L KL 个选择 a i a_i ai 的位置一定是一个前缀。
  • 因此,我们可以只在状态中计入考虑了的位置数,决策都选的位置数,决策选 b i b_i bi 位置数,则显然有 O ( 1 ) O(1) O(1) 转移。
  • 此部分做法可以参见下方代码 185 185 185 行起的内容。
  • 将最优的决策点打表,可以发现所有 “都选” 的决策都排在 “选 b i b_i bi” 的决策之前,因此,若枚举一个分界点,我们可以将问题分成两个子问题进行求解。
  • 上述动态规划的时间复杂度就变为了 O ( N 2 ) O(N^2) O(N2) ,而处理 “选 b i b_i bi” 的决策只需要用一个堆即可做到 O ( N L o g N ) O(NLogN) O(NLogN)
  • 此部分做法可以参见下方代码 123 123 123 行起的内容。
  • 上述算法的时间复杂度瓶颈在于动态规划,考虑再将 “都选” 的决策点集合打表,可以发现长度为 i i i 的前缀对应的最优决策点的集合与长度为 i + 1 i+1 i+1 的前缀对应的最优决策点的集合至多相差一个元素。
  • 因此,只要用堆维护决策点集合,贪心地进行替换即可。
  • 时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN)

【代码】

// O(N Log N)
#include
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -f;
	for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
	x *= f;
}
int n, m, l;
bool sel[MAXN];
ll dp[MAXN], sa[MAXN];
pair <int, int> a[MAXN];
priority_queue <int> q;
struct Heap {
	priority_queue <pair <ll, int> > Heap, Delt;
	void clear() {
		while (!Heap.empty()) Heap.pop();
		while (!Delt.empty()) Delt.pop();
	}
	void ins(pair <ll, int> x) {
		Heap.push(x);
	}
	int size() {
		return Heap.size() - Delt.size();
	}
	void del(pair <ll, int> x) {
		Delt.push(x);
	}
	pair <ll, int> query() {
		while (!Heap.empty() && !Delt.empty() && Heap.top() == Delt.top()) {
			Heap.pop();
			Delt.pop();
		}
		if (Heap.empty()) return make_pair(-2000000000, 0);
		else return Heap.top();
	}
} Heap, Heaq;
int main() {
	freopen("sequence.in", "r", stdin);
	freopen("sequence.out", "w", stdout);
	int T; read(T);
	while (T--) {
		read(n), read(m), read(l);
		for (int i = 1; i <= n; i++)
			read(a[i].first);
		for (int i = 1; i <= n; i++)
			read(a[i].second);
		sort(a + 1, a + n + 1);
		reverse(a + 1, a + n + 1);
		memset(dp, -1, sizeof(dp));
		for (int i = 1; i <= n; i++)
			sa[i] = sa[i - 1] + a[i].first;
		Heap.clear();
		ll cur = 0;
		for (int i = 1; i <= m; i++) {
			cur += a[i].second;
			Heap.ins(make_pair(-a[i].second, i));
			if (Heap.size() > l) {
				cur += Heap.query().first;
				Heap.del(Heap.query());
			}
			if (i >= l) dp[i] = sa[i] + cur;
		}
		memset(sel, false, sizeof(sel));
		for (int i = 1; i <= l; i++) {
			sel[Heap.query().second] = true;
			Heap.del(Heap.query());
		}
		Heap.clear(), Heaq.clear();
		int point = 0;
		for (int i = 1; i <= m; i++)
			if (!sel[i]) point = i;
		for (int i = 1; i <= m; i++)
			if (sel[i]) {
				if (i > point) Heap.ins(make_pair(-a[i].first - a[i].second, i));
				else Heaq.ins(make_pair(-a[i].second, i));
			}
		for (int i = m + 1; i <= n; i++) {
			ll inc = a[i].first + a[i].second;
			dp[i] = dp[i - 1];
			if (Heap.query().first > Heaq.query().first - a[point].first && Heap.query().first + inc > 0) {
				dp[i] += Heap.query().first + inc;
				Heap.del(Heap.query());
				Heap.ins(make_pair(-inc, i));
			} else if (point != 0 && Heap.query().first <= Heaq.query().first - a[point].first && Heaq.query().first - a[point].first + inc > 0) {
				dp[i] += Heaq.query().first - a[point].first + inc; point--;
				sel[i] = true, sel[Heaq.query().second] = false;
				Heaq.del(Heaq.query());
				while (point > 0 && sel[point]) {
					Heap.ins(make_pair(-a[point].first - a[point].second, point));
					Heaq.del(make_pair(-a[point].second, point));
					point--;
				}
				Heap.ins(make_pair(-inc, i));
			}
		}
		while (!q.empty()) q.pop();
		ll ans = 0, now = 0;
		if (m == l) ans = dp[n];
		for (int i = n; i >= 1; i--) {
			now += a[i].second;
			q.push(-a[i].second);
			if (q.size() > m - l) {
				now += q.top();
				q.pop();
			}
			if (q.size() == m - l) {
				ll add = 0;
				if (i - 1 < m) add = sa[m] - sa[i - 1];
				chkmax(ans, dp[i - 1] + now + add);
			}
		}
		cout << ans << endl;
	}
	return 0;
}
// O(N ^ 2)
#include
using namespace std;
const int MAXN = 2e3 + 5;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -f;
	for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
	x *= f;
}
int n, m, l;
ll dp[MAXN][MAXN], sa[MAXN];
pair <int, int> a[MAXN];
priority_queue <int> q;
int main() {
	freopen("sequence.in", "r", stdin);
	freopen("sequence.out", "w", stdout);
	int T; read(T);
	while (T--) {
		read(n), read(m), read(l);
		for (int i = 1; i <= n; i++)
			read(a[i].first);
		for (int i = 1; i <= n; i++)
			read(a[i].second);
		sort(a + 1, a + n + 1);
		reverse(a + 1, a + n + 1);
		memset(dp, -1, sizeof(dp));
		dp[0][0] = 0;
		for (int i = 0; i <= n - 1; i++)
		for (int j = 0; j <= l; j++) {
			ll tmp = dp[i][j];
			if (tmp != -1) {
				if (j < l) chkmax(dp[i + 1][j + 1], tmp + a[i + 1].first + a[i + 1].second);
				int add = (i + 1 - j <= m - l) ? (a[i + 1].first) : 0;
				chkmax(dp[i + 1][j], tmp + add);
			}
		}
		while (!q.empty()) q.pop();
		ll ans = 0, now = 0;
		if (m == l) ans = dp[n][m];
		for (int i = 1; i <= n; i++)
			sa[i] = sa[i - 1] + a[i].first;
		for (int i = n; i >= 1; i--) {
			now += a[i].second;
			q.push(-a[i].second);
			if (q.size() > m - l) {
				now += q.top();
				q.pop();
			}
			if (q.size() == m - l) {
				ll add = 0;
				if (i - 1 < m) add = sa[m] - sa[i - 1];
				chkmax(ans, dp[i - 1][l] + now + add);
			}
		}
		cout << ans << endl;
	}
	return 0;
}
// O(N ^ 3)
#include
using namespace std;
const int MAXN = 155;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -f;
	for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
	x *= f;
}
int n, m, l;
ll dp[MAXN][MAXN][MAXN];
pair <int, int> a[MAXN];
int main() {
	freopen("sequence.in", "r", stdin);
	freopen("force.out", "w", stdout);
	int T; read(T);
	while (T--) {
		read(n), read(m), read(l);
		for (int i = 1; i <= n; i++)
			read(a[i].first);
		for (int i = 1; i <= n; i++)
			read(a[i].second);
		sort(a + 1, a + n + 1);
		reverse(a + 1, a + n + 1);
		memset(dp, -1, sizeof(dp));
		dp[0][0][0] = 0;
		for (int i = 0; i <= n - 1; i++)
		for (int j = 0; j <= l; j++)
		for (int k = 0; k <= m - l; k++) {
			ll tmp = dp[i][j][k];
			if (tmp != -1) {
				if (j < l) chkmax(dp[i + 1][j + 1][k], tmp + a[i + 1].first + a[i + 1].second);
				int add = (i + 1 - j <= m - l) ? (a[i + 1].first) : 0;
				if (k < m - l) chkmax(dp[i + 1][j][k + 1], tmp + a[i + 1].second + add);
				chkmax(dp[i + 1][j][k], tmp + add);
			}
		}
		cout << dp[n][l][m - l] << endl;
	}
	return 0;
}

你可能感兴趣的:(【OJ】LOJ,【类型】做题记录,【算法】动态规划,【算法】找规律,【算法】贪心,【数据结构】堆)