CSP历年复赛题-P8816 [CSP-J 2022] 上升点列
原题链接:https://www.luogu.com.cn/problem/P8816
题意解读:二维平面中有n个点,从某个点开始,只能往右或者往下,计算能选中的最长连续的点数,可以任意增加m个点的位置使得“上升点列”连续。
解题思路:
1、考虑m=0的情况
这时就是在n个点中选最长上升点列,由于上升点列必须距离是1,且只能往右或者往下延伸
可以将所有点先排序,先按x小到大排,x相等按y小到大排
设f[i]表示以第i个点结尾的最长上升点列
可以类比LIS的模型推出转移方程:
f[i] = max(f[i], f[j] + 1) , 1<= j <=i-1,且点i和点j之间的距离是1且j->i上升
初始化:f[i] = 1
这样可以获得40分。
40分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 505;
struct node
{
int x, y;
};
node a[N];
bool cmp(node a, node b)
{
if(a.x != b.x) return a.x < b.x;
return a.y < b.y;
}
int f[N]; //f[i]表示以a[i]结尾的最长上升点列的长度
int n, m, ans;
//检查a到b的距离是否为1,需满足a在b右或者下
bool check(node a, node b)
{
if(a.x == b.x) return a.y - b.y == 1; //a在右
if(a.y == b.y) return a.x - b.x == 1; //a在下
return false;
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> a[i].x >> a[i].y;
sort(a + 1, a + n + 1, cmp);
for(int i = 1; i <= n; i++)
{
f[i] = 1;
for(int j = 1; j <= i - 1; j++)
{
if(check(a[i], a[j]))
{
f[i] = max(f[i], f[j] + 1);
}
ans = max(ans, f[i]);
}
}
cout << ans;
return 0;
}
2、考虑m!=0的情况
既然增加了一种状态,就在状态定义时增加一维:
设f[i][j]表示以i点结尾,且插入了j个点时的最长上升点列长度;
同样,枚举倒数第二个点k :1 ~ i-1,
计算点k到i的距离:d = a[i].y - a[k].y + a[i].x - a[k].x,k到i的距离是d,就要插入d-1个点
则有转移方程:f[i][j] = max(f[i][j], f[k][j-d+1] + d),d - 1 <= j <= m
这里在判断倒数第二个点是否合法是与上面情况不同,不需要判断距离是1,只需要判断k点y坐标不增即可,因为可以插入点
判断逻辑:if(a[k].y > a[i].y) continue;
初始化:f[i][j] = j + 1,以i结尾插入j个点的最长上升点列长度至少是j+1
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 505;
struct node
{
int x, y;
};
node a[N];
bool cmp(node a, node b)
{
if(a.x != b.x) return a.x < b.x;
return a.y < b.y;
}
int f[N][N]; //f[i][j]表示以a[i]结尾,插入了j个点的最长上升点列的长度
int n, m, ans;
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> a[i].x >> a[i].y;
sort(a + 1, a + n + 1, cmp);
for(int i = 1; i <= n; i++)
{
for(int j = 0; j <= m; j++)
{
f[i][j] = j + 1; //初始化,以i点结尾的点列长度至少可以插入j个点+自己
}
}
for(int i = 1; i <= n; i++) //枚举最后一个点
{
for(int k = 1; k <= i - 1; k++) //枚举倒数第二个点
{
if(a[k].y > a[i].y) continue; //不符合上升点列,i前面的点k坐标x能单调不减,坐标y不能保证
int d = a[i].y - a[k].y + a[i].x - a[k].x; //i和k之间的距离
for(int j = d - 1; j <= m; j++) //枚举插入的点数,至少是d-1
{
f[i][j] = max(f[i][j], f[k][j - d + 1] + d);
}
}
}
for(int i = 1; i <= n; i++) ans = max(ans, f[i][m]);
cout << ans;
return 0;
}