【动态规划、dp】[CSP-J 2022] 上升点列 题解

题目描述

在一个二维平面内,给定 n n n 个整数点 ( x i , y i ) (x_i, y_i) (xi,yi),此外你还可以自由添加 k k k 个整数点。

你在自由添加 k k k 个点后,还需要从 n + k n + k n+k 个点中选出若干个整数点并组成一个序列,使得序列中任意相邻两点间的欧几里得距离恰好为 1 1 1 而且横坐标、纵坐标值均单调不减,即 x i + 1 − x i = 1 , y i + 1 = y i x_{i+1} - x_i = 1, y_{i+1} = y_i xi+1xi=1,yi+1=yi y i + 1 − y i = 1 , x i + 1 = x i y_{i+1} - y_i = 1, x_{i+1} = x_i yi+1yi=1,xi+1=xi。请给出满足条件的序列的最大长度。

【数据范围】

保证对于所有数据满足: 1 ≤ n ≤ 500 1 \leq n \leq 500 1n500 0 ≤ k ≤ 100 0 \leq k \leq 100 0k100。对于所有给定的整点,其横纵坐标 1 ≤ x i , y i ≤ 10 9 1 \leq x_i, y_i \leq {10}^9 1xi,yi109,且保证所有给定的点互不重合。对于自由添加的整点,其横纵坐标不受限制。

思路

考虑动态规划,记 d p i , j dp_{i,j} dpi,j 表示以第 i i i 个点为结尾,已经插入 j j j 个自由点的最长长度。

首先将点对按照 x , y x,y x,y 大小排个序,确保转移时横纵坐标比当前节点小的点对都在它前面,否则无法转移。

容易发现, d p i , j = max ⁡ l = 1 i − 1 d p l , j − d i s ( i , l ) + 1 + d i s ( i , l ) + 1 , ( y i ≥ y l , j − d i s ( i , l ) + 1 ≥ 0 ) dp_{i,j} = \max_{l=1}^{i - 1}{dp_{l,j -dis(i,l)+1} + dis(i,l)+1},(y_i \geq y_l,j -dis(i,l)+1 \geq 0) dpi,j=maxl=1i1dpl,jdis(i,l)+1+dis(i,l)+1,(yiyl,jdis(i,l)+10), d p i , j dp_{i,j} dpi,j 初始值赋值为 j + 1 j+1 j+1(全部用在它前面)。

解释:因为两个点中间因插入的点对数量等于它们欧几里得距离减去一(因为头尾都已经有了),文中 d i s ( i , l ) dis(i,l) dis(i,l) 表示的就是两者的欧几里得距离剪去 1 1 1。计算方式如下:

int dis(int a,int b) {
	return abs(edge[a].x - edge[b].x) + abs(edge[a].y - edge[b].y) - 1;
}

最后输出的答案就是 a n s ans ans 就是 d p i , j + ( k − j ) dp_{i,j} + (k - j) dpi,j+(kj) 的最大值(加上 k − j k-j kj 代表剩下所有没用完的直接加在该点后面)
在这里插入图片描述

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k;
struct node{
	int x,y;
	friend bool operator <(node a,node b) {
		if(a.x == b.x) return a.y < b.y;
		return a.x < b.x; 
	}
}edge[505];
int dp[505][105],ans = -1e18;
int dis(int a,int b) {
	return abs(edge[a].x - edge[b].x) + abs(edge[a].y - edge[b].y) - 1;
}
signed main() {
	scanf("%lld %lld",&n,&k);
	for(int i = 1;i <= n;i++) scanf("%lld%lld",&edge[i].x,&edge[i].y);
	sort(edge + 1,edge + n + 1);
	for(int i = 1;i <= n;i++) {
		for(int j = 0;j <= k;j++) {
			dp[i][j] = j + 1;
			for(int l = 1;l < i;l++) {
				int d = dis(i,l);
				if(edge[l].y <= edge[i].y and d <= j) {
					dp[i][j] = max(dp[i][j],dp[l][j - d] + d + 1);
				}
			}
			ans = max(ans,dp[i][j] + k - j);
		}
	}
	printf("%lld\n",ans);
    return 0;
}
  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值