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;
}

 

posted @ 2024-06-20 10:34  五月江城  阅读(141)  评论(0编辑  收藏  举报