UVA 1490 Let the light guide us 题解

更好的阅读体验

题意

有一个 n×m 的网格,每个格子有两个值 ab(原题中是 Tf)。你需要在每一行选择一个格子,使得对于任意选择的两个格子 (i,j),(i+1,k),满足 |jk|b(i,j)+b(i+1,k)。求所选格子的 a 之和的最小值。

n100,m5000,0b(i,j)105

思路

朴素 DP

首先,显然 b>5000 是没有意义的(因为 |jk| 是不可能大于 5000 的)。

然后可以设计出一个状态:f(i,j) 表示考虑了前 i 行,第 i 行选了第 j 列的格子,最小的代价。转移方程是这样的:

f(i,j)=a(i,j)+min|jk|b(i,j)+b(i1,k)f(i1,k)

时间复杂度 O(nm2)

优化

这个状态基本上已经无法优化了(反正我不会),所以我们要在转移上下手。

我们看这个式子:

|jk|b(i,j)+b(i1,k)

显然右边大于 0,所以可以把它化成这样:(两个式子同时成立)

{jkb(i,j)+b(i1,k)kjb(i,j)+b(i1,k)

化简得:

{jb(i,j)k+b(i1,k)j+b(i,j)kb(i1,k)

接下来就比较神奇了。(鬼知道这是怎么想到的)

我们记 SL=jb(i,j)SR=j+b(i,j),并记区间 S=[SL,SR]
类似的,记 TL=kb(i1,k)TR=k+b(i1,k),并记区间 T=[TL,TR]
(显然 SLSRTLTR

那么上面的式子就可以写成:

{SLTRTLSR

那么这个式子和「ST 相交」是等价的。

证明:

区间 ST 不相交 等价于 SR<TLTR<SL,所以 ST 相交 等价于:{SRTLTRSL
这与原式相同。

然后就简单了。现在的问题是对于每个格子有一个区间 [SL,SR],如果相邻两行的格子的区间相交,那么这两个格子就可以同时被选(即可以用上一行格子的 dp 值更新这一行的 dp 值)。

做法显然是对于每一行开一个线段树,将上一行的 [TL,TR] 区间取 min,这一行的 [SL,SR] 区间求 min,就是区间改区间查的问题了。

n 个线段树空间会炸,但其实可以只用开一个线段树。

时间复杂度 O(nmlogm)

代码

// UVA1490
#include <cstdio>
#include <algorithm>
#include <cstring>

const int N = 100 + 5;
const int M = 5000 + 5;
const int ARR_V = 7000 + 5;
const int V_MAX = 6000;
const int INF = 0x3f3f3f3f;

int a[N][M], b[N][M];
int n, m;

int f[N][M];

struct SegmentTree {
	int t[ARR_V << 3], lazy[ARR_V << 3];
	void reset() {
		memset(t, 0, sizeof(t));
		memset(lazy, INF, sizeof(lazy));
	}
	void build() {
		memset(t, INF, sizeof(t));
		memset(lazy, INF, sizeof(lazy));
	}
	void set_lazy(int x, int v) { t[x] = std::min(t[x], v), lazy[x] = std::min(lazy[x], v); }
	void lazy_down(int x) {
		set_lazy(x << 1, lazy[x]), set_lazy(x << 1 | 1, lazy[x]);
		lazy[x] = INF;
	}
	int query(int ql, int qr, int x = 1, int l = 1, int r = V_MAX << 1) {
		if(ql > qr) return INF;
		if(ql <= l && r <= qr) return t[x];
		int mid = (l + r) >> 1, ret = INF;
		lazy_down(x);
		if(ql <= mid) ret = std::min(ret, query(ql, qr, x << 1, l, mid));
		if(qr > mid) ret = std::min(ret, query(ql, qr, x << 1 | 1, mid + 1, r));
		return ret;
	}
	void modify(int ql, int qr, int qv, int x = 1, int l = 1, int r = V_MAX << 1) {
		if(ql > qr) return;
		if(ql <= l && r <= qr) { set_lazy(x, qv); return; }
		int mid = (l + r) >> 1;
		lazy_down(x);
		if(ql <= mid) modify(ql, qr, qv, x << 1, l, mid);
		if(qr > mid) modify(ql, qr, qv, x << 1 | 1, mid + 1, r);
		t[x] = std::min(t[x << 1], t[x << 1 | 1]);
	}
} seg;

int main() {
	while(scanf("%d%d", &n, &m) == 2 && (n | m)) {
		for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) scanf("%d", &a[i][j]);
		for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) scanf("%d", &b[i][j]), b[i][j] = std::min(b[i][j], 5000);
		seg.reset();
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= m; j++)
				f[i][j] = seg.query(j - b[i][j] + V_MAX, j + b[i][j] + V_MAX) + a[i][j];
			seg.build();
			for(int j = 1; j <= m; j++)
				seg.modify(j - b[i][j] + V_MAX, j + b[i][j] + V_MAX, f[i][j]);
		}
		int ans = INF;
		for(int j = 1; j <= m; j++) ans = std::min(ans, f[n][j]);
		printf("%d\n", ans);
	}
	return 0;
}
posted @   XxEray  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示