LOJ3664 「JOI 2022 Final」选举

LOJ3664 「JOI 2022 Final」选举

题目大意

题目链接

\(N\) 个州,每个州有两个参数:\(A_i\), \(B_i\),满足 \(A_i\leq B_i\)

你在第 \(i\) 个州演讲大于等于 \(A_i\) 小时可以获得一张选票,演讲大于等于 \(B_i\) 小时可以获得一位协作者(因为 \(A_i\leq B_i\),获得协作者的同时意味着一定获得了选票)。有些 \(B_i = -1\) 的州表示无法获得协作者。协作者可以帮你演讲,效果和你自己演讲一样。多个人可以同时在同一个州演讲,比如 \(3\) 个人一起在某个州演讲了 \(2\) 小时,就算 \(6\) 个小时。

请求出获得 \(K\) 张选票所需的最短时间。输出实数,要求误差不超过 \(0.01\)

数据范围:\(1\leq K\leq N\leq 500\)\(1\leq A_i\leq 1000\)\(A_i\leq B_i\leq 1000\)\(B_i = -1\)

本题题解

设我们最终在 \(a\) 个州仅获得了选票,在 \(b\) 个州获得了选票和协作者,在其余的 \(c\) 个州里什么也没有得到。称这三种州分别为 \(\texttt{A}\),\(\texttt{B}\),\(\texttt{C}\) 类州。显然,\(a + b = K\), \(a + b + c = N\)

假设我们已经确定了每个州的种类,如何安排演讲使用时最短呢?显然可以采用如下贪心策略:先按 \(B_i\) 从小到大的顺序,在所有 \(\texttt{B}\) 类州里演讲,则第 \(i\) 个进行到的州将耗时 \(\frac{B_i}{i}\) (\(1\leq i\leq b\))。然后再在所有 \(\texttt{A}\) 类州里演讲,第 \(i\) 个州将耗时 \(\frac{A_i}{b + 1}\)(与顺序无关)。

不妨先将所有州按 \(B_i\) 排好序。我们枚举 \(b\),然后进行 DP。设 \(\text{dp}(i, j, k)\) 表示考虑了前 \(i\) 个州,总共获得了 \(j\) 张选票,其中有 \(k\) 个州获得了协作者。每个州有三种转移:要么不选(用时不变,转移到 \(\text{dp}(i + 1, j, k)\)),要么选择获得选票(用时增加 \(\frac{A_{i + 1}}{b + 1}\),转移到 \(\text{dp}(i + 1, j + 1, k)\)),要么同时获得选票和协作者(用时增加 \(\frac{B_{i + 1}}{k + 1}\),转移到 \(\text{dp}(i + 1, j + 1, k + 1)\))。最后答案为 \(\text{dp}(N, K, b)\)。这个 DP 的时间复杂度是 \(\mathcal{O}(N^3)\),因为外层还要枚举 \(b\),总时间复杂度 \(\mathcal{O}(N^4)\),无法通过。

这说明我们前面的贪心策略,研究得还不是很完备,需要继续观察问题本身的特性。不妨考虑最后两个 \(\texttt{B}\) 类州,假设它们之间有一个 \(\texttt{C}\) 类州,那么把最后一个 \(\texttt{B}\) 类州和该 \(\texttt{C}\) 类州的种类互换,一定能使答案更优(因为其它州的用时不变,而最后一个 \(\texttt{B}\) 类州的用时减少了)。同理,可以证明任意两个 \(\texttt{B}\) 类州之间都不存在 \(\texttt{C}\) 类州,并且第一个 \(\texttt{B}\) 类州前也没有 \(\texttt{C}\) 类州。换言之,以最后一个 \(\texttt{B}\) 类州为界,序列的前半部分只有 \(\texttt{A}\)\(\texttt{B}\) 类州,后半部分只有 \(\texttt{A}\)\(\texttt{C}\) 类州。

这个特性让我们可以优化原来的 DP。设 \(\text{dp}_2(i, j)\) 表示考虑了前 \(i\) 个州,选定了 \(j\)\(\texttt{B}\) 类州,的最少用时。在这个 DP 中,\(j = b\) 时我们不再让它往后转移,也就是它只计算到最后一个 \(\texttt{B}\) 类州就停止,后半部分不归它管。DP 完成后,我们还需要在后半部分里选几个 \(\texttt{A}\) 类州,显然直接选 \(A_i\) 最小的若干个即可。具体来说,设后 \(x\) 个州里,\(A_i\) 最小的 \(y\) 个州的 \(A_i\) 之和为 \(w(x, y)\)。那么答案就是 \(\min_{i = b}^{K}\{\text{dp}_2(i, b) + w(N - i, K - i)\}\)。这个 DP 的时间复杂度是 \(\mathcal{O}(N^2)\)\(w\) 显然也可以在同样的复杂度内预处理出来。此外,外层还要枚举 \(b\),所以总时间复杂度 \(\mathcal{O}(N^3)\)

参考代码

// problem: LOJ3664
#include <bits/stdc++.h>
using namespace std;

#define mk make_pair
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

template<typename T> inline void ckmax(T& x, T y) { x = (y > x ? y : x); }
template<typename T> inline void ckmin(T& x, T y) { x = (y < x ? y : x); }

const int MAXN = 500;
const int INF = 1e9;
int n, K, lim;
struct State {
	int a, b;
} s[MAXN + 5], sa[MAXN + 5][MAXN + 5];
bool cmpB(State s1, State s2) {
	return s1.b < s2.b;
}
bool cmpA(State s1, State s2) {
	return s1.a < s2.a;
}
double dp[MAXN + 5][MAXN + 5];

int main() {
	cin >> n >> K;
	lim = n;
	for (int i = 1; i <= n; ++i) {
		cin >> s[i].a >> s[i].b;
		if (s[i].b == -1) {
			s[i].b = INF;
			--lim; // max number of collaborators
		}
	}
	
	sort(s + 1, s + n + 1, cmpB); // sort by B
	
	for (int i = 1; i <= n; ++i) { // the last i states, sorted by A
		for (int j = 1; j <= i; ++j) {
			sa[i][j] = s[n - j + 1];
		}
		sort(sa[i] + 1, sa[i] + i + 1, cmpA);
	}
	
	double ans = INF;
	for (int x = 0; x <= lim && x <= K; ++x) { // number of collaborators
		
		for (int i = 0; i <= n; ++i)
			for (int j = 0; j <= n; ++j)
				dp[i][j] = INF;
		dp[0][0] = 0;
		for (int i = 1; i <= lim && i <= K; ++i) {
			for (int j = 0; j < i && j < x; ++j) { // how many collaborators made in the first (i - 1) states
				// make a collaborator in state i
				// dp[i - 1][j] -> dp[i][j + 1]
				ckmin(dp[i][j + 1], dp[i - 1][j] + (double)s[i].b / (j + 1));
				
				// don't make any collaborator in state i
				// dp[i - 1][j] -> dp[i][j]
				ckmin(dp[i][j], dp[i - 1][j] + (double)s[i].a / (x + 1));
			}
		}
		
		for (int p = x; p <= lim && p <= K; ++p) { // the position of the last collaborator
			// select (K - p) states from the last (n - p) states
			double cost = 0;
			for (int i = 1; i <= K - p; ++i) {
				cost += (double)sa[n - p][i].a / (x + 1);
			}
			ckmin(ans, dp[p][x] + cost);
		}
	}
	cout << setiosflags(ios :: fixed) << setprecision(10) << ans << endl;
	return 0;
}
posted @ 2022-02-26 16:56  duyiblue  阅读(473)  评论(2编辑  收藏  举报