NO.212 小R的投影面积优化
问题描述
小R在二维平面上绘制了一些点,这些点的横纵坐标都是正整数,并且每个点的横坐标都是唯一的。他将这些点按横坐标从左到右依次连接,形成了一条折线,与横坐标轴共同构成了一个投影区域。为了尽可能增大这个区域的面积,小R想要通过交换两个点的纵坐标来获得最大的投影面积。
你的任务是帮助小R选择两个点交换它们的纵坐标,获得最大的投影面积。需要注意的是:
- 如果无需交换纵坐标来增大面积,则输出
-1。 - 如果有多个符合条件的交换方案,选择第一个横坐标较小的方案;如果仍然有多个方案,选择第二个横坐标较小的方案。
测试样例
样例1:
输入:
n = 3 ,pts = [[1, 2], [4, 1], [2, 5]]
输出:[1, 4]
样例2:
输入:
n = 2 ,pts = [[1, 2], [3, 2]]
输出:[-1]
样例3:
输入:
n = 4 ,pts = [[1, 10], [2, 20], [3, 30], [4, 5]]
输出:[-1]
题目解析
【爆力枚举】
我们可以先计算没有交换点之前的面积 original_area ,并将它视为此时的最大面积 max_area 然后遍历交换方案,每次交换之后,从头开始重新计算总面积 new_area 。若此时新的总面积 new_area 大于原最大面积 max_area ,则更新最大面积,并记录此时交换方案的横坐标。此后继续遍历交换方案,重复以上步骤。
最终,若 max_area 与 original_area 不同,则意味着最大面积有所改变,即有可以增大投影面积的交换方案,则返回该方案对应的两个纵坐标;若 max_area 与 original_area 相同,则意味着没有可以增大投影面积的交换方案,则返回'[-1]'。
定义变量如下:
交换方案前的总面积:original_area
最大面积:max_area
交换方案后的总面积:new_area
记录最佳交换方案的返回值:vector<int>rst
代码实现1
#include <iostream>
#include <numeric>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
double area_calculate(vector<int> pt1, vector<int> pt2){//计算pt1和pt2与横坐标轴共同形成投影区域面积
double area = double(pt1[1] + pt2[1]) * abs(pt1[0] - pt2[0]) / 2;
return area;
}
vector<int> solution(int n, vector<vector<int>> pts) {
sort(pts.begin(),pts.end(),[](const vector<int>& a, const vector<int>& b) {
return a[0] < b[0];
});
double original_area = 0;
for(int i = 0;i < n-1;i++){
original_area+=area_calculate(pts[i], pts[i+1]);
}
double max_area = original_area; //定义最大面积
vector<int>rst = {0,0}; //返回的坐标点
for(int j = 0;j < n-1;j++){
for(int k = j+1;k < n;k++){
double new_area = 0;
swap(pts[j][1], pts[k][1]); //交换x=j和x=k的点的纵坐标
for(int i = 0;i < n-1;i++){ //计算交换后方案的面积
new_area+=area_calculate(pts[i], pts[i+1]);
}
if(max_area<new_area){ //若符合条件,则记录
max_area=new_area;
rst[0]=pts[j][0];
rst[1]=pts[k][0];
}
swap(pts[j][1], pts[k][1]); //交换x=j和x=k的点的纵坐标,恢复到交换前的图像
}
}
if(max_area != original_area) return rst;
else return {-1};
}
注意:
-
此处单独使用一个函数 area_calculate(vector pt1, vector pt2) 用来计算两个点与横坐标轴共同形成投影面积的大小
-
由于提供点的数据不是严格按照横坐标升序,所以需要我们根据横坐标进行升序排序,此处用 sort 进行排序。由于 pts 是一个二维vector结构,所以在 swap 的 cmp 参数设置如下:
sort(pts.begin(),pts.end(),[](const vector<int>& a, const vector<int>& b) { return a[0] < b[0]; }); -
由于面积涉及浮点计算,所以统一用 double 数据类型
-
题目要求:如果有多个符合条件的交换方案,选择第一个横坐标较小的方案;如果仍然有多个方案,选择第二个横坐标较小的方案。在本方案中由于遍历是从横坐标从小到大进行的,所以横坐标较小的点会优先被记录,且后续若出现相同投影面积的方案,也不会更新记录点。
优化
- 【暴力枚举】方案的时间复杂度为O(),复杂度较高,面对大量数据时效果不佳
- 优化思路:无需关注整体变化,仅关注互换纵坐标的两个点周围的投影面积变化即可
【优化算法】
我们可以只考虑交换方案两个点各自前后相邻区域的投影面积变化之和是否大于最大的变化量,若大于,则更新最大变化量并记录交换方案的坐标。最终,若最大变化量为0,则意味着没有可以增大投影面积的交换方案,则返回[-1];否则,返回最佳交换方案的对应横坐标
定义变量如下:
最大面积变化量:max_area
交换方案后的面积变化量:sum
记录最佳交换方案的返回值:vector<int>rst
横坐标较小的点的相邻左侧面积、相邻右侧面积:pre_area1, post_area1
横坐标较大的点的相邻左侧面积、相邻右侧面积:pre_area2, post_area2
代码实现2
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
double area_calculate(vector<int> pt1, vector<int> pt2){ //计算pt1和pt2与横坐标轴共同形成投影区域面积
double area = double(pt1[1] + pt2[1]) * abs(pt1[0] - pt2[0]) / 2;
return area;
}
vector<int> solution(int n, vector<vector<int>> pts) {
sort(pts.begin(),pts.end(),[](const vector<int>& a, const vector<int>& b) {
return a[0] < b[0];
});
double max_sum=0;
vector<int>rst={0,0}; //返回的坐标点
for(int j=0;j<n-1;j++){
for(int k=j+1;k<n;k++){
double sum2=0;
//计算交换x=pts[j][0]和x=pts[k][0]的纵坐标前 x=j 前后图像面积 和 x=k 前后图像面积
double pre_area1, post_area1, pre_area2, post_area2; //分别对应,横坐标较小的点的相邻左侧面积、相邻右侧面积,横坐标较大的点的相邻左侧面积、相邻右侧面积
pre_area1 = post_area1 = pre_area2 = post_area2 = 0;
if(j==0){ //横坐标小的交换点的左侧没有形成梯形
pre_area1 = 0;
}else{
pre_area1 = area_calculate(pts[j-1], pts[j]);
}
if(k==n-1){ //横坐标大的交换点的右侧没有形成梯形
post_area2 = 0;
}else{
post_area2 = area_calculate(pts[k], pts[k+1]);
}
post_area1 = area_calculate(pts[j], pts[j+1]);
pre_area2 = area_calculate(pts[k-1], pts[k]);
swap(pts[j][1], pts[k][1]); //交换x=j和x=k的点的纵坐标
if(j==0){
// pre_area1 = 0; 无变化
}else{
sum2+=area_calculate(pts[j-1], pts[j]) - pre_area1;
}
if(k==n-1){
// post_area2 = 0; 无变化
}else{
sum2+=area_calculate(pts[k], pts[k+1]) - post_area2;
}
sum2+=area_calculate(pts[j], pts[j+1]) - post_area1;
sum2+=area_calculate(pts[k-1], pts[k]) - pre_area2;
if(sum2 > max_sum){ //若符合条件,则记录
max_sum=sum2;
rst[0]=pts[j][0];
rst[1]=pts[k][0];
}
swap(pts[j][1], pts[k][1]); //交换x=j和x=k的点的纵坐标,恢复到交换前的图像
}
}
if(max_sum!=0) return rst;
else return {-1};
}
经过优化,本题实现方案的时间复杂度降低到O()