[acwing面向模型编程]最长上升子序列模型

题目1:1012. 友好城市

题意:

Palmia国有一条横贯东西的大河,河有笔直的南北两岸,岸上各有位置各不相同的N个城市。

北岸的每个城市有且仅有一个友好城市在南岸,而且不同城市的友好城市不相同。

每对友好城市都向政府申请在河上开辟一条直线航道连接两个城市,但是由于河上雾太大,政府决定避免任意两条航道交叉,以避免事故。

编程帮助政府做出一些批准和拒绝申请的决定,使得在保证任意两条航线不相交的情况下,被批准的申请尽量多。

输入格式
第1行,一个整数N,表示城市数。

第2行到第n+1行,每行两个整数,中间用1个空格隔开,分别表示南岸和北岸的一对友好城市的坐标。

输出格式
仅一行,输出一个整数,表示政府所能批准的最多申请数。

数据范围
1≤N≤5000,
0≤xi≤10000
输入样例:
7
22 4
2 6
10 3
15 12
9 8
17 17
4 2
输出样例:
4

分析:

这个问题难在模型的转化,考虑两个直线l1 l2,只有当l2的两个坐标都大于或者小于l1的时候,两者才不会相交。所以这个题需要先对某一岸的数值排序,然后求对岸的LIS即可

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 lll;
#define print(i) cout << "debug: " << i << endl
#define close() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define mem(a, b) memset(a, b, sizeof(a))
const ll mod = 1e9 + 7;
const int maxn = 2e6;
const int inf = 0x3f3f3f3f;
struct node
{
    int a, b;
    node(int a = 0, int b = 0) : a(a), b(b){}
}a[maxn];
int dp[maxn];

int main()
{
    int n; cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i].a >> a[i].b;
    sort(a + 1, a + 1 + n, [](node a, node b){
        return a.a < b.a;
    });
    for(int i = 1; i <= n; i++)
    {
        dp[i] = 1;
        for(int j = 1; j < i; j++)
            if(a[j].b < a[i].b)
                dp[i] = max(dp[i], dp[j] + 1);
    }
    int maxx = 0;
    for(int i = 1; i <= n; i++)
        maxx = max(maxx, dp[i]);
    cout << maxx << endl;
}

题目2:272. 最长公共上升子序列

分析:

dp[i][j]代表在[1, i]和[1, j]中的且以j结尾的最长公共上升子序列的长度。

(引用课上的图片,侵删)
dp[i][j]就是由椭圆形的划分转移过来。
这里重点说一下优化部分。
当a[i] = a[j]的时候,我们要用k遍历[1, j - 1]找到b[k] < b[j] = a[i]的j,求max(dp[i - 1][k]),这里因为i是不变的,因此我们可以用一个变量来维护这个max.

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 lll;
#define print(i) cout << "debug: " << i << endl
#define close() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define mem(a, b) memset(a, b, sizeof(a))
const ll mod = 1e9 + 7;
const int maxn = 3e3 + 10;
const int inf = 0x3f3f3f3f;
int a[maxn], b[maxn];
int dp[maxn][maxn];

int main()
{
    int n; cin >> n;
	for(int i = 1; i <= n; i++)
		cin >> a[i];
	for(int i = 1; i <= n; i++)
		cin >> b[i];
	int res = 0;
	for(int i = 1; i <= n; i++)
	{
		int maxv = 0;
		for(int j = 1; j <= n; j++)
		{
			dp[i][j] = dp[i - 1][j];
			if(a[i] == b[j]) dp[i][j] = max(dp[i][j], maxv + 1);
			if(b[j] < a[i]) maxv = max(maxv, dp[i - 1][j]);
		}
	}
	for(int i = 1; i <= n; i++) res = max(res, dp[n][i]);
	cout << res << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值