友好城市:
题目:
Palmia国有一条横贯东西的大河,河有笔直的南北两岸,岸上各有位置各不相同的N个城市。
北岸的每个城市有且仅有一个友好城市在南岸,而且不同城市的友好城市不相同。每对友好城市都向政府申请在河上开辟一条直线航道连接两个城市,但是由于河上雾太大,政府决定避免任意两条航道交叉,以避免事故。编程帮助政府做出一些批准和拒绝申请的决定,使得在保证任意两条航线不相交的情况下,被批准的申请尽量多。
输入格式
第1行,一个整数N,表示城市数。
第2行到第n+1行,每行两个整数,中间用1个空格隔开,分别表示南岸和北岸的一对友好城市的坐标。
输出格式
仅一行,输出一个整数,表示政府所能批准的最多申请数。
输入样例:
7
22 4
2 6
10 3
15 12
9 8
17 17
4 2
输出样例:
4
问题分析:
先对数据进行录入, 那么我们可以得到一组以下关系的数据(左为南坐标, 右为北坐标):
这个时候数据展示的还不是很清晰, 我们先对两边的数据进行排序, 得到一组更加直观的数据, 即现实中城市的排列情况, 如下图:
如果你是市长, 你需要在航线不交叉的前提下, 确保使用最多的航线, 那么我们得出的航线如下(划去代表不选该航线)
说明在不交叉航线的前提下, 最多可以开辟4条航线, 那么我们是怎么实现的呢?其实是最长上升子序列长度问题, 把数据看成下图, 只对左边的坐标进行升序排序, 在交换的时候, 所对应的右边的坐标也需要进行位置交换, 因为是一一对应关系的, 左边是升序排序, 当右边有两个坐标是降序排序时, 是不是说明这里会在现实航线中出现交叉航线. 那么我们可以对右边的坐标进行求最长上升子序列长度, 如图:
问题转化成为求上升子序列最大长度的问题, 就变得相对简单了, 实现代码如下:
#include <iostream>
#include <string>
#include <stdlib.h>
#include <iomanip>
#include <cmath>
#include <algorithm>
using namespace std;
/*
该问题可以先将一岸的坐标先进行排序(小到大),
之后再对另一岸的坐标进行查找最长升序子序列长度
当我们发现另一岸的坐标不是升序排序, 说明有桥会交叉
*/
int main() {
int n;
cin >> n;
// 数据初始化, a数组存储南岸坐标, b数组存储北岸坐标
int a[5005], b[5005];
for (int i = 0; i < n; i++) {
cin >> a[i] >> b[i];
}
// 功能: 对南岸进行冒泡排序, 所对应的北岸友好城市也需要排序
for (int i = 0; i < n; i++) {
for (int j = 1; j < n - i; j++) {
if (a[j - 1] > a[j]) {
swap(a[j - 1], a[j]);
swap(b[j - 1], b[j]);
}
}
}
// 功能: 计算最长升序子序列
// d数组存储所对应的点, 升序子序列的长度
int d[5001];
for (int i = 0; i < n; i++) {
d[i] = 1;
for (int j = 0; j < i; j++) {
if (b[j] < b[i]) { // 找出i之前, 比i位置的坐标要小的坐标
d[i] = max(d[i], d[j] + 1); // 状态转移方程
}
}
}
// 功能: 求最长子序列, 即最大值
int max_number = -1;
for (int i = 0; i < n; i++) {
if (d[i] > max_number) {
max_number = d[i];
}
}
cout << max_number;
return 0;
}