题目
[CSP-J 2022] 上升点列
题目描述
在一个二维平面内,给定 n 个整数点 (x i ,y i ),此外你还可以自由添加 k 个整数点。
你在自由添加 k 个点后,还需要从 n+k 个点中选出若干个整数点并组成一个序列,使得序列中任意相邻两点间的欧几里得距离恰好为 1 而且横坐标、纵坐标值均单调不减,即 x i+1 −x i =1,y i+1 =y i 或 y i+1 −y i =1,x i+1 =x i 。请给出满足条件的序列的最大长度。
输入格式
第一行两个正整数 n,k 分别表示给定的整点个数、可自由添加的整点个数。接下来 n 行,第 i 行两个正整数 x i ,y i 表示给定的第 i 个点的横纵坐标。
输出格式
输出一个整数表示满足要求的序列的最大长度。
输入输出样例
输入 #1
8 2
3 1
3 2
3 3
3 6
1 2
2 2
5 5
5 3
输出 #1
8
输入 #2
4 100
10 10
15 25
20 20
30 30
输出 #2
103
思路
1.各点按x、y排序
2.状态:到达各点的最多序列
3.状态转移:到达该点的最多序列=前面所有点中的最多序列
4.转移方程:f[i][n]=max(f[i][n],f[j][k-dis(i,j)]+dis(i,j)+1)
f[当前点][借的点数]=max(f[当前点][借的点数],
f[当前点前的每个点][当前借的点数-i到j两点间需要的点数]
+
i到j两点间需要的点数
+
i点本身
)
5.三层循环
一层,遍历每个点(i)
二层,遍历i前所有点(j)
三层,遍历能借的点数0到k-dis(i,j)
图解

两点间欧几里得距离

代码
#include <bits/stdc++.h>
using namespace std;
int n,//整数点数
k,//可添加整数点数
x,y,//整数点的坐标
ans,//最多序列
f[501][101];//在借得不同数点情况下到达每个点的最多序列数
//f[i][x]=max(f[i][x],f[j][x-dis(i,j)]+dis(i,j)+1)
struct point{
int x,y;
bool operator<(const point p2)const{
if(x==p2.x)return y<p2.y;
return x<p2.x;
}
}p[501];
int dis(int i2,int i1){
return p[i1].x-p[i2].x+p[i1].y-p[i2].y-1;//两点间加的点,不包括端点,要-1
}
void view(){
cout<<"各点"<<endl;
for(int i=0;i<n;i++)cout<<p[i].x<<","<<p[i].y<<endl;
}
void view2(){
cout<<"最多序列"<<endl;
cout<<"添加\t";for(int x=0;x<=k;x++)cout<<x<<'\t';cout<<endl;
for(int i=0;i<n;i++){
cout<<p[i].x<<","<<p[i].y<<"\t";
for(int x=0;x<=k;x++)cout<<f[i][x]<<'\t';
cout<<endl;
}
}
int main(){
freopen("point.in","r",stdin);
cin>>n>>k;
for(int i=0;i<n;i++){
cin>>x>>y;
p[i]=point{x,y};
}
//view();
sort(p,p+n);
view();
for(int i=0;i<n;i++){//遍历所有点
f[i][0]=1;//初始化,每个点序列起码有1
for(int j=0;j<i;j++)//遍历前面的所有点
if(p[j].y<=p[i].y){//如果是升序(右上)
int m=dis(j,i);//欧几里得距离
for(int x=0;x+m<=k;x++)//j已经加的点+现在i到j加的点不超过k
f[i][x+m]=max(f[i][x+m],f[j][x]+dis(j,i)+1);
/*
第i点用了x+m个点后的最多序列=
以下中最大
本来的最多序列
i前j点用了x个点后的最多序列,加上i到j需要的点,+i点本身
*/
}
}
view2();
for(int i=0;i<n;i++)//遍历所有点,不一定是最后一个序列最多
for(int j=0;j<=k;j++)//用添加点最少而序列最多的
ans=max(ans,f[i][j]+k-j);//在必须用的点基础上还可以用k剩余的点
cout<<ans;
return 0;
}
数据

CSP - J 2022上升点列问题的算法解析
1741

被折叠的 条评论
为什么被折叠?



