首页 > 其他分享 >P4766 [CERC2014]Outer space invaders

P4766 [CERC2014]Outer space invaders

时间:2023-01-16 23:24:57浏览次数:68  
标签:le Outer 忍术 算法 operatorname invaders CERC2014 id 黄金

目录

知识点:区间 DP

链接:Luogu

可能更加的阅读体验:My blog

简述

有 \(n\) 匹黄金船赶来侵略地球。第 \(i\) 匹黄金船会在时间 \(a_i\) 出现在距离你 \(d_i\) 的位置,被消灭的时间不能大于 \(b_i\),否则你就会被外星怪马抓回她的母星每天做十万次马儿跳。在任一时刻你可以释放一次距离为 \(R\) 的忍术,花费 \(R\) 的代价将距离你不大于 \(R\) 的黄金船干掉。求不被抓走前提下干掉所有黄金船的最小代价。
\(T\) 组数据,每组数据给定长度为 \(n\) 的三个数列 \(a,b,d\),描述了一次外星怪马的侵略,求抵挡这次侵略的最小代价。
\(1\le n\le 300\),\(1\le a_i<b_i\le 10^4\),\(1\le d_i\le 10^4\)。
题面中没有给出 \(T\) 的范围,但经测试 \(T\) 并不影响总复杂度。
2S,128MB。

假算法一

我想到一个线性 DP!

显然只在恰好 \(b_i\) 时刻释放忍术是最优的。考虑先将黄金船按 \(b_i\) 升序排序,这样在后面的忍术对是否消灭了前面的黄金船没有影响。设 \(f_i\) 表示消灭前 \(i\) 匹黄金船的最小代价,转移时枚举最后一次释放忍术的时刻 \(b_j\),考虑最后一次释放忍术把 \(j\sim i\) 这一段黄金船全部消灭,则在满足 \(\left( \max_{k=j}^{i} a_i \le b_j\right)\) 条件下,有:

\[f_i = \min_{j= 1}^{i}\left( f_{j - 1}+ \max_{k = j}^{i} d_k\right) \]

答案即 \(f_n\),总复杂度 \(O(n)\) 级别。

然而假了。虽然排序后后面的忍术不会影响前面的黄金船,但前面的忍术会影响到后面的黄金船。具有后效性,无法进行线性 DP。

真算法一

发现 \(n\) 较小,但 \(a,b\) 较大,考虑先离散化,记离散化后最大的时刻为 \(m\)。

按时间顺序枚举具有后效性,则考虑对时间维进行区间 DP。设 \(f_{i,j}\) 表示将满足 \(i\le a_p < b_p\le j\) 的黄金船 \(p\) 全部消灭的代价。转移时考虑最后在位置 \(k(i\le k\le j)\) 进行的一次操作,在这次操作之前消灭了 \([a_p,b_p]\subseteq [i,k-1]\) 和 \([a_p, b_p]\subseteq [k+1,i]\) 中的所有黄金船 \(p\),这次操作消灭了满足 \(i\le a_p \le k \le b_p\le j\) 的全部黄金船,即有:

\[f_{i,j} = \min_{k=i}^{j}\left( f_{i, k - 1} + f_{k + 1, j} + d_{i,j,k}\right) \]

上式中 \(d_{i,j,k}\) 表示满足:\(i\le a_p \le k \le b_p\le j\) 的黄金船 \(p\) 中最远的距离,即:

\[d_{i,j,k} = \max_{i\le a_p \le k \le b_p\le j} d_p \]

最终答案即 \(f_{1,m}\)。

发现 DP 的过程是 \(O(n^3)\) 级别的,但预处理 \(d\) 是 \(O(n^4)\) 级别的,不预处理则 DP 变为 \(O(n^4)\) 级别,无法通过本题。

真算法二

发现复杂度瓶颈出在取最大值的过程中。取最大值的目的是得到 \([a_p, b_p]\) 跨越区间分界点 \(k\) 的最大的 \(d_p\),计算出使得 \([i,j]\) 中的黄金船全部被消灭的最后一次操作的代价。

发现操作的顺序并不影响正确性,我们不妨钦定满足 \([a_p,b_p]\subseteq [i,j]\) 的 \(d_p\) 最大的黄金船是在最后一次操作中被消灭的。转移时先求得这匹黄金船的编号 \(\operatorname{id}\),则枚举的分界点 \(k\) 被限定在了 \([a_{\operatorname{id}}, b_{\operatorname{id}}]\) 中,有:

\[f_{i,j} = \min_{k=a_{\operatorname{id}}}^{b_{\operatorname{id}}}\left( f_{i, k - 1} + f_{k + 1, j} + d_{\operatorname{id}}\right) \]

仅需在枚举 \(k\) 之前 \(O(n)\) 地求得 \(\operatorname{id}\) 即可。答案仍为 \(f_{1,m}\)。总复杂度 \(O(n^3)\) 级别,可以通过本题。

真算法三

真算法二和其他题解中的写法已经非常类似了,但其他题解中在转移时没有分界点 \(k\) 的限制,为什么仍然得到了正确的答案呢?换言之,怎么保证 \(k\notin [a_{\operatorname{id}}, b_{\operatorname{id}}]\) 的转移一定不优于 \(k\in [a_{\operatorname{id}}, b_{\operatorname{id}}]\) 的转移呢?

需要指出的是,真算法二实际上是对真算法一的一种减去无用转移的优化。我们考虑一次 \(k\notin [a_{\operatorname{id}}, b_{\operatorname{id}}]\) 的转移 \(f_{i,j} = \min\left( f_{i, k - 1} + f_{k + 1, j} + d_{\operatorname{id}}\right)\)。此时有 \([a_{\operatorname{id}}, b_{\operatorname{id}}] \subseteq [i,k-1]\) 或 \([a_{\operatorname{id}}, b_{\operatorname{id}}]\subseteq [k+1, j]\),若这里的 \(f_{i,j}\) 定义不变,则最大的代价 \(d_{\operatorname{id}}\) 已经在 \(f_{i,k-1}\) 或 \(f_{k+1,j}\) 中被计算了一次了。更进一步地,在真算法一中,我们在计算 \(f_{i,j}\) 时选择的是 \(k\in [a_p, b_p]\) 的最大的代价 \(d_p\),显然有 \(d_p\le d_{\operatorname{id}}\)。当我们在真算法二中进行 \(k\in [a_{\operatorname{id}}, b_{\operatorname{id}}]\) 的转移时,\(d_p\) 的贡献也是已经被包含在了 \(f_{i,k-1}\) 或 \(f_{k+1,j}\) 中了。

总结一下,\(k\notin [a_{\operatorname{id}}, b_{\operatorname{id}}]\) 的转移实质上都是 \(k\in [a_{\operatorname{id}}, b_{\operatorname{id}}]\) 的转移重复状态,把它们都考虑上也并不会影响正确性。

代码

真算法二

//By:Luckyblock
/*
知识点:区间 DP
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
const int kN = 300 + 10;
const int kD = kN << 1;
const int Inf = 1e9;
//=============================================================
int n, l[kN], r[kN], d[kN], f[kD][kD];
int dnum, data[kD]; 
//=============================================================
inline int read() {
	int f = 1, w = 0; char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
	for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
	return f * w;
}
void Init() {
	n = read();
	for (int i = 1; i <= n; ++ i) {
		l[i] = read(), r[i] = read(), d[i] = read();
		data[i] = l[i], data[i + n] = r[i];
	}
	dnum = 0;
	std::sort(data + 1, data + 2 * n + 1);
	for (int i = 1; i <= 2 * n; ++ i) {
		if (data[i] != data[i - 1]) {
			data[++ dnum] = data[i];
		}
	}
	for (int i = 1; i <= n; ++ i) {
		l[i] = std::lower_bound(data + 1, data + dnum + 1, l[i]) - data;
		r[i] = std::lower_bound(data + 1, data + dnum + 1, r[i]) - data;
	}
}
void DP() {
	for (int i = 1; i <= dnum; ++ i) {
		for (int j = 1; j <= dnum; ++ j) {
			f[i][j] = Inf;
		}
	}
	for (int lth = 1; lth <= dnum; ++ lth) {
		for (int ll = 1, rr = ll + lth - 1; rr <= dnum; ++ ll, ++ rr) {
			int maxd = 0, maxdid = 0;
			for (int k = 1; k <= n; ++ k) {
				if (ll <= l[k] && r[k] <= rr) {
					if (d[k] > maxd) maxd = d[k], maxdid = k;
				}
			}
			if (!maxd) {
				f[ll][rr] = 0;
				continue;
			}
			for (int k = l[maxdid]; k <= r[maxdid]; ++ k) {
				f[ll][rr] = std::min(f[ll][rr], 
														 f[ll][k - 1] + f[k + 1][rr] + maxd);
			}
		}
	}
}
//=============================================================
int main() {
	int T = read();
	while (T --) {
		Init();
		DP();
		printf("%d\n", f[1][dnum]);
	}
	return 0;
}

标签:le,Outer,忍术,算法,operatorname,invaders,CERC2014,id,黄金
From: https://www.cnblogs.com/luckyblock/p/17056671.html

相关文章

  • 【vue-router】动态组件中通过路由参数来多次调用同一个页面中遇到的坑
    vue2router的几个坑总结需求:vue2动态路由中不同菜单目录引用同一个组件页面,通过给接口传入不同的参数来区分页面。vue通过切换路由如果仅仅query或者params参数发生......
  • this.$router.push 新窗口打开
    /eventwarn/detail?id=xxxxxdetail(id){ letrouterJump=this.$router.resolve({path:'/eventwarn/detail',query:{id}})......
  • vue-router-路由
    目录前言安装一个简单的demoMain和Content组件配置路由index.js挂载路由main.jsApp.vue效果图前言项目地址:https://gitee.com/cnleika/vue-learning/tree/master/notebo......
  • uni-app 使用uni-simple-router进行路由守卫
    //1.安装依赖//uni-read-pages适用于读取page.json文件中的路由信息[email protected]//2.配置与初始化//2.1根目录新建vue.c......
  • 【vue】Vue-router
    Vue-router安装npminstallvue-router--save-devvue-cli中已经选择安装了vue-router,那这里不需要重复安装了解读route路径```src/router/index.js``import......
  • vue-router的使用
    vue-router是vue基础工具的重要组成部分。通过简单的配置路由组件映射关系,可以实现vue页面轻松跳转。 什么是前端路由它是URL地址与组件之间的对应关系,通常使用Hash地......
  • vue-router
    vue-router实现页面的跳转在cmd中输入:npminstallvue-router--save-dev如果报错则使用:cnpminstallvue-router--save-dev运行程序(如果报错则降低vue-route......
  • Vue-Router
    Rouer组件可以构造单页引用多页应用:mpa每一个页面都是一个html文件方便seo优化单页应用:spa知乎网站掘进百度移动端单页用于取决情况:1.用户群体比较......
  • vue-router
    vue:V2.5.2vue-router版本:V3.0.1//获取原型对象上的push函数constoriginalPush=VueRouter.prototype.push//修改原型对象中的push方法VueRouter.prototype.push=......
  • beforeRouterLeave this.$route.meta,keepALive=true ;第一次进入不生效,第二次进入生
    今天写业务代码的时候,页面缓存之后,清除缓存总不生效,具体代码如下:  我最后把beforeRouterLeave改成了BeforRouterEnter ,然后就生效了;很大的可能是因为,beforeRoute......