P8816 [CSP-J 2022] 上升点列 题解

P8816

提供一种不一样的做法。

首先将每个点以横坐标为第一关键字,纵坐标为第二关键字排序。

一维的 dp 肯定不够,因为 dp 既要存最多点数,又要保存自由点的点数。

赛时没看 k 的范围,于是开了一个结构体。

dpi.w 表示从当前起点开始且于 i 点结束的最多的点数,dpi.ned 表示达到点数所需的最少的自由点的点数。

从其左下部分的点转移即可。

但如果直接转移,这个 dp 的正确性无法保证,即左部点稀疏而右部点稠密时就会出错。

枚举一个起点 st,就可以保证 dp 的正确性了。

因为原 dp 出错只能是该点到起点的距离太长,以至于连接后面的稠密点时,自由点不足,此时正解的所选点都在右部的稠密点。

而枚举起点则确定了剩余的所选点只能在右部,相当于枚举了右部点,解决了左疏右密的问题。

时间复杂度:O(n3)

代码:

const int N = 5e2;
int n, k;
struct point {
	int x, y;
} a[N + 5];
struct DP {
	int w, ned;
} dp[N + 5];
bool cmp(point x, point y) {
	return x.x != y.x ? x.x < y.x : x.y < y.y;
}
bool check(int p1, int p2) {
	return (a[p1].x > a[p2].x && a[p1].y >= a[p2].y) || (a[p1].x >= a[p2].x && a[p1].y > a[p2].y);
}
int dis(int p1, int p2) {
	return abs(a[p1].x - a[p2].x) + abs(a[p1].y - a[p2].y);
}
int main() {
	cin >> n >> k;
	for(int i = 1; i <= n; i++)
	    cin >> a[i].x >> a[i].y;
	sort(a + 1, a + n + 1, cmp);
	int ans = 0;
	for(int st = 1; st <= n; st++) {
		memset(dp, 0, sizeof(dp));
		dp[st].w = 1, dp[st].ned = 0;
		for(int i = st + 1; i <= n; i++)
			for(int j = st; j < i; j++) {
				int nedd = dis(i, j) + dp[j].ned - 1;
				if(check(i, j) && nedd <= k && (dp[i].w <= dp[j].w || (dp[i].w == dp[j].w + 1 && nedd < dp[i].ned)))
					dp[i].w = dp[j].w + 1, dp[i].ned = nedd;
			}
		for(int i = 1; i <= n; i++) ans = max(ans, dp[i].w);
	}
	cout << ans + k << "\n";
	return 0;
posted @   Pengzt  阅读(100)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示