P8162 [JOI 2022 Final] 让我们赢得选举 (贪心+dp)

P8162 [JOI 2022 Final] 让我们赢得选举

upd 2024.11.14

贪心+dp

看完题目,觉得挺难。随着时间推移,协作者们可能在不同位置,怎么考虑?

如果考虑协作者们一起做事,会不会最优呢?事实证明是会的。所以同一时刻有且只有一个州在演讲。这就简化了思考量。

现在怎么考虑演讲的顺序?一个发现是如果最终获得了若干协作者,那么按 Bi 从小到大的顺序演讲一定最优。

那么我们就可以考虑将序列按照 Bi 排序,我们的演讲就是从左到右的。现在可以考虑 dp 了吧。设状态 fi,j,k 表示考虑到前 i 个州,有 j 张选票,k 个协作者的最小耗时。

复杂度太高了,怎么优化?观察协作者的分布,如果一个协作者前面有没有选票的州,那么这位协作者完全可以换成在前面获得。并不影响后面的时间。画个图。

你可以发现前面是一段一定有选票的前缀!那么如果当前只考虑前缀,我们就只需要知道协作者的个数就可以了。

剩下的后面的部分一定是 Ai 最小的前几个,用个背包也可以求。

题目要求最小耗时,可以考虑贪心和dp。

先考虑贪心。首先,假如我们此时有 b 个州得到了选票和协作者,那么下一次演讲一定是 b 个协作者和自己一起去同一个州演讲,时间为 ai/bib+1,这样我们的时间一定不会浪费掉。

接下来,若我们已经知道了方案,此时已知每个州是得到选票(A 类)、得到选票+协作者(B 类)、不演讲(C 类)中的一种,我们又可以继续贪心。设有 bB 类州,那么我们一定是按从小到大的顺序最先得到所有 B 类州A 类点的答案就是 aib+1只受 b 的影响),这样做时间一定最短。

于是启发我们把序列按 bi 从小到大排序,这样在考虑到第 i 个是 B 类州时,不会受到后面的影响。接下来可以枚举 b,对每个 b 求解。考虑 dp。

fi,j,k 表示考虑到第 i 个,有 j 张选票,有 k 个协作者的最小时间。转移显然。那么 dp 的复杂度是 O(n3),加上枚举的 b,总复杂度是 O(n4)

考虑优化,继续贪心, 我们两个 B 类州之间有 C 类州,那么我们可以把右端的 B 类州与之间任意 C 类州互换,这样一定不差。以此类推就有 B 类州之间不存在 C 州,且序列最左端也不会有 C 类州。于是我们可以把序列分成两部分,左部分全是 AB 类,右部分一定是 A 类或 C 类。如果我们只对左部分 dp,就可以省去 j 这一维了(因为左部分都有选票)。枚举断点 i,显然右部分的答案就是 [i+1,n] 中选 kiai 的最小和,这个也可以用背包 O(n2) 处理。于是每个 b 的答案就是 mini=1n(f(i,b)+g(i+1,ki)b+1)。dp 复杂度降到 O(n2),总复杂度是 O(n3)

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back

typedef long long i64;
const int N = 510;
int n, k;
struct node {
	int a, b;
} a[N];
bool cmp(node a, node b) {
	return a.b < b.b;
}
double f[N][N], g[N][N];
void Solve() {
	std::cin >> n >> k;

	int B = 0;
	for(int i = 1; i <= n; i++) {
		std::cin >> a[i].a >> a[i].b;
		if(a[i].b != -1) B++;
		a[i].b = ((a[i].b == -1) ? 0x3f3f3f3f : a[i].b);
	}

	std::sort(a + 1, a + n + 1, cmp);
	double ans = 0x3f3f3f3f;
	for(int i = 1; i <= n + 1; i++) {
		for(int j = 1; j <= n + 1; j++) g[i][j] = 600000;
	}
	for(int i = 1; i <= n + 1; i++) g[i][0] = 0;
	for(int i = n; i >= 1; i--) {
		for(int j = 1; j <= n - i + 1; j++) {
			g[i][j] = 600000;
			g[i][j] = std::min(g[i][j], g[i + 1][j]);
			g[i][j] = std::min(g[i][j], g[i + 1][j - 1] + a[i].a);
		}
	}
	for(int b = 0; b <= std::min(B, k); b++) {
		for(int i = 0; i <= n; i++) {
			for(int j = 0; j <= n; j++) f[i][j] = 600000;
		}
		f[0][0] = 0;
		for(int i = 1; i <= k; i++) {
			for(int j = 0; j <= b; j++) {
				f[i][j] = std::min(f[i][j], f[i - 1][j] + (double)(1.0 * a[i].a / (b + 1)));
				if(j - 1 >= 0) f[i][j] = std::min(f[i][j], f[i - 1][j - 1] + (double)(1.0 * a[i].b / j));
			}
		}
		for(int i = b; i <= k; i++) {
			ans = std::min(ans, f[i][b] + (double)(1.0 * g[i + 1][k - i] / (b + 1)));
		}
	}
	printf("%.15lf\n", ans);
}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	Solve();

	return 0;
}
posted @   Fire_Raku  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示