P8816 [CSP-J 2022] 上升点列 题解
提供一种不一样的做法。
首先将每个点以横坐标为第一关键字,纵坐标为第二关键字排序。
一维的 dp 肯定不够,因为 dp 既要存最多点数,又要保存自由点的点数。
赛时没看
从其左下部分的点转移即可。
但如果直接转移,这个 dp 的正确性无法保证,即左部点稀疏而右部点稠密时就会出错。
枚举一个起点
因为原 dp 出错只能是该点到起点的距离太长,以至于连接后面的稠密点时,自由点不足,此时正解的所选点都在右部的稠密点。
而枚举起点则确定了剩余的所选点只能在右部,相当于枚举了右部点,解决了左疏右密的问题。
时间复杂度:
代码:
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;
标签:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现