P8816 [CSP-J 2022] 上升点列

· · 题解

说实话这道题最为最后一题稍微简单了,这篇题解将从部分分到正解把我的赛时想法展现出来。

1.$ $k=0

看完题面,这道题好像最长上升子序列的模板题哎!那我们肯定先把所有的点按坐标从左到右再从下至上排序。接下来应该怎么办呢?
要是没有添加点的操作就可以直接 O(n^2) 做了呢。看一看有 45k=0 的分耶!于是我们设 dp_i 表示以第排序后的第 i 个点的最大长度。
显然 dp_i=\max_{j=1}^i{dp_j+1},其中 j 一定要判断可以到达 i 才可以转移。

2.$ $x_i,y_i\le100

然后我又看向了 x_i,y_i\le100 的点,这两档分加起来就有 75 了呢!于是我们设 f_{i,j,l} 表示以坐标 (i,j) 作为最后一个点,且一共添加了 l 个点的最大长度。
如果 (i,j) 本身存在的话,f_{i,j,l}=\max(f_{i-1,j,l}+1,f_{i,j-1,l}+1)。 如果 (i,j) 不在给定的点中,那么 f_{i,j,l}=\max(f_{i-1,j,l-1}+1,f_{i,j-1,l-1}+1)

基于第一部分,我们可以之间枚举当前点从哪个点转移过来,显然两个点之间需要添加的点的个数是确定的,我们设 $c_{i,l}$ 表示以第 $i$ 个点作为结尾,且添加了 $l$ 个点的最大长度。显然 $c_{i,l}=\max_{j=1}^i{c_{j,l-d}+d+1}$,其中 $j$ 一定要判断可以到达 $i$ 才可以转移,$d$ 表示点 $j$ 到 $i$ 之间需要添加的点数,$d=x_i-x_j+y_i-y_j-1$。注意最后一个点不一定是已经存在的点,所以还要枚举一下把剩下还可以添加的点加在点 $i$ 后面的长度。 最后附上赛时代码。其实只需要 $work3$ 这个函数就可以啦,另外两个是部分分。 ```cpp #include<algorithm> #include<iostream> #include<cstring> #include<cstdio> using namespace std; int n,k,ans,dp[105],f[105][105][105],c[105][105],mx;bool p[105][105]; struct node{ int x,y; bool operator < (node b)const{ if(x==b.x)return y<b.y; return x<b.x; } }a[505]; void work1(){ for(int i=1;i<=n;i++){ dp[i]=1; for(int j=1;j<i;j++) if(a[i].x-a[j].x==1&&a[i].y==a[j].y||(a[i].y-a[j].y==1&&a[i].x==a[j].x))dp[i]=max(dp[i],dp[j]+1); ans=max(ans,dp[i]); } } void work2(){ for(int i=1;i<=n;i++)p[a[i].x][a[i].y]=1; for(int i=1;i<=100;i++) for(int j=1;j<=100;j++){ if(p[i][j]){ for(int l=0;l<=k;l++){ f[i][j][l]=max(f[i-1][j][l]+1,f[i][j][l]); f[i][j][l]=max(f[i][j-1][l]+1,f[i][j][l]); ans=max(ans,f[i][j][l]); } }else{ for(int l=1;l<=k;l++){ f[i][j][l]=max(f[i-1][j][l-1]+1,f[i][j][l]); f[i][j][l]=max(f[i][j-1][l-1]+1,f[i][j][l]); ans=max(ans,f[i][j][l]); } } } } void work3(){ for(int i=1;i<=n;i++){ c[i][0]=1; for(int j=i-1;j>=1;j--){ if(a[j].x>a[i].x||a[j].y>a[i].y)continue ; int x=a[i].x-a[j].x+a[i].y-a[j].y-1; for(int l=x;l<=k;l++)c[i][l]=max(c[j][l-x]+x+1,c[i][l]); } for(int l=0;l<=k;l++)ans=max(c[i][l],ans); } for(int i=1;i<=n;i++) for(int j=0;j<=k;j++) ans=max(ans,c[i][j]+(k-j)); } int main(){ scanf("%d%d",&n,&k); for(int i=1;i<=n;i++){ scanf("%d%d",&a[i].x,&a[i].y); mx=max(a[i].x,a[i].y); } sort(a+1,a+n+1); if(k==0)work1(); else if(mx<=100)work2(); else work3(); printf("%d",ans); return 0; } ```