洛谷:P8816
OJ:P4977
n等于8,k等于2时,结果为8
输入:
8 2
3 1
3 2
3 3
3 6
1 2
2 2
5 5
5 3
输出:
8
代码说明:
n
个给定的整数点和 k
个可以自由添加的点。a
中,随后按点的横坐标 x
进行排序,若 x
坐标相同则按 y
坐标排序,确保在构造序列时横纵坐标递增。f[i][j]
来表示以第 i
个点为结尾,且允许添加 j
个点的情况下所能构造的最长序列。f[i][j] = j + 1
,即可以构造的序列至少包含当前点和 j
个可添加的点。p
,判断点 p
能否与点 i
相连(要求横纵坐标不减),并计算点 p
和点 i
之间所需填充的点数 d
。如果所需点数 d
不超过允许的添加点数 j
,则更新 f[i][j]
的最大值。f[i][j] = max(f[i][j], f[p][j - d] + d + 1)
。f[p][j - d]
:表示在允许添加 j - d
个点的情况下,以第 p
个点为结尾的最长合法序列的长度(p
是在 i
点之前的某个点)。d
:表示第 p
个点和第 i
个点之间需要插入的点数。这个 d
的计算公式是 d = a[i].x - a[p].x + a[i].y - a[p].y - 1
,即横纵坐标的差之和减去1。d
反映了如果要从点 p
移动到点 i
,中间需要插入多少个点,才能保持相邻点的欧几里得距离为1。i
,取所有以 i
为结尾的序列中最大长度的序列。代码实现一:
结构体排序+动规
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | /**************************************************************** * Description: 2022 CSP-J 复赛第4题 上升点列 * Author: Alex Li * Date: 2023-10-05 16:32:31 * LastEditTime: 2024-10-18 10:38:15 ****************************************************************/ #include <iostream> #include <algorithm> #include <utility> #include <vector> using namespace std; const int maxn=505; // 最大的整点数 const int maxk=105; // 最大的可自由添加的整点数 int n, k; // n表示给定的整点数,k表示可添加的整点数 // 定义一个结构体node,用来表示二维平面上的点 struct node { int x; // 点的横坐标 int y; // 点的纵坐标 } a[maxn]; // 用来存储所有的点,包括输入的n个点 // f[i][j] 表示以第i个点为序列的结尾,且允许添加j个点时,所能构成的最长序列长度 int f[maxn][maxk]; // 自定义的比较函数,按照x坐标排序,若x坐标相同则按y坐标排序 bool cmp(node a, node b) { if (a.x != b.x) return a.x < b.x; // 按x坐标升序排序 else return a.y < b.y; // 若x相同,按y坐标升序排序 } int main() { // 输入点的数量n和可添加的点的数量k cin >> n >> k; // 输入每个点的坐标 for (int i = 1; i <= n; i++) cin >> a[i].x >> a[i].y; // 对点进行排序,先按x,再按y sort(a + 1, a + n + 1, cmp); // 动态规划部分 for (int i = 1; i <= n; i++) { // 遍历每一个点 for (int j = 0; j <= k; j++) { // 遍历每一个可能添加的点数 f[i][j] = j + 1; // 初始值为当前可以添加的点数加1(自身点) // 对于每一个前面的点p,尝试从点p转移到点i for (int p = 1; p < i; p++) { // 判断点p是否可以与点i相连,要求点p的横纵坐标都小于等于点i if (a[p].x <= a[i].x && a[p].y <= a[i].y) { int d = a[i].x - a[p].x + a[i].y - a[p].y - 1; // 计算p和i之间需要填充的点数d // 如果所需添加的点数d不超过允许的点数j,则进行状态转移 if (d <= j) f[i][j] = max(f[i][j], f[p][j - d] + d + 1); // 动态转移方程,计算最大值 } } } } // 计算最大值,遍历所有以i为结尾的序列,取最大值 int ans = 0; for (int i = 1; i <= n; i++) ans = max(ans, f[i][k]); // 输出最终结果 cout << ans; return 0; } |
代码实现二:
pair排序+动规
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /**************************************************************** * Description: 2022 CSP-J 复赛第4题 pair结构 * Author: Alex Li * Date: 2023-10-05 16:32:31 * LastEditTime: 2024-10-19 17:17:54 ****************************************************************/ #include <iostream> #include <algorithm> using namespace std; const int maxn=505; const int maxk=105; int n,k; pair<int,int> a[maxn]; //表示平面上每个点,有两坐标 int f[maxn][maxk]; //f[i][j]表示以第i点为结尾且允许添加j个点时的最长答案,枚举点i之前可以添加的所有节点。 int main(){ cin>>n>>k; for (int i = 1; i <=n; i++)cin>>a[i].first>>a[i].second; sort(a+1,a+n+1);//从数组1下标开始 for (int i = 1; i <=n; i++){ for (int j = 0; j <=k; j++){ f[i][j]=j+1; for (int p = 1; p <i; p++) if(a[p].first<=a[i].first&&a[p].second<=a[i].second){ int d=a[i].first-a[p].first+a[i].second-a[p].second-1; if(d<=j)f[i][j]=max(f[i][j],f[p][j-d]+d+1); } } } int ans=0; for(int i=1;i<=n;i++)ans=max(ans,f[i][k]); cout<<ans; return 0; } |