BZOJ4614/UVA1742 Oil 计算几何

传送门

题意:在平面直角坐标系中给出$N$条互不相交的、与$x$轴平行、且在$x$轴上方的线段,每一条线段的价值为其长度。求一条不与$x$轴平行的直线,使得与这条直线相交的线段的价值之和最大,求出这个和。$N \leq 2000 , -10^6 \leq \text{线段两端点横坐标} \leq 10^6 , 1 \leq \text{线段两端点纵坐标} \leq 10^6$


首先我们可以想到:必定存在一条最优的直线,它经过某一条线段的左端点和某条线段的右端点,因为如果某条最优的直线不满足这些性质,我们可以通过旋转和平移使其满足条件。基于上面的结论,我们可以很轻松地想到一种算法:枚举直线经过哪一条线段的左端点和哪一条线段的右端点,然后扫一遍所有的线段看其是否被该直线穿过。这样子的复杂度是$O(n^3)$的。

接着我们考虑优化。我们这样考虑:枚举直线经过其左端点的线段,那么某一条线段会产生贡献当且仅当直线的斜率在某一个区间之内。那么我们将所有线段的会产生贡献的区间算出来,问题就转化为了:给定若干区间,每个区间有权值,选择一个点使得包含这个点的区间的权值之和最大。这个问题可以利用尺取法:按照左端点从小到大加入区间,删除与新加入的区间不相交的区间,计算剩余的权值和。这种算法的复杂度就降为了$O(n^2logn)$

一个细节:为了避免区间不连续、斜率不存在的情况,我们可以计算每一个线段产生贡献时,直线斜率的倒数会落在哪一段区间$($我们认为直线的斜率不存在的时候,斜率的倒数为$0)$,这样每一个线段的贡献区间就是连续的,还可以在计算斜率时避免除$0$。

 1 //一份常数极大的Code
 2 #include<bits/stdc++.h>
 3 #define ld long double
 4 #define eps 1e-12
 5 using namespace std;
 6 
 7 inline int read(){
 8     int a = 0;
 9     bool f = 0;
10     char c = getchar();
11     while(!isdigit(c)){
12         if(c == '-')
13             f = 1;
14         c = getchar();
15     }
16     while(isdigit(c)){
17         a = (a << 3) + (a << 1) + (c ^ '0');
18         c = getchar();
19     }
20     return f ? -a : a;
21 }
22 
23 struct range{
24     ld l , r;
25     int ind;
26 }now[2010] , ano[2010];
27 int num[2010][3];
28 
29 inline long long max(long long a , long long b){
30     return a > b ? a : b;
31 }
32 
33 inline void swap(int &a , int &b){
34     int t = a;
35     a = b;
36     b = t;
37 }
38 
39 inline void swap(ld &a , ld &b){
40     ld t = a;
41     a = b;
42     b = t;
43 }
44 
45 bool cmp1(range l , range r){
46     return l.l < r.l;
47 }
48 
49 bool cmp2(range l , range r){
50     return l.r < r.r;
51 }
52 
53 inline ld calcK(int x1 , int y1 , int x2 , int y2){
54     return (ld)(x1 - x2) / (y1 - y2);
55 }
56 
57 int main(){
58     int N;
59     while(scanf("%d" , &N) != EOF){
60         for(int i = 1 ; i <= N ; i++){
61             num[i][0] = read();
62             num[i][1] = read();
63             num[i][2] = read();
64             if(num[i][1] < num[i][0])
65                 swap(num[i][1] , num[i][0]);
66         }
67         long long maxN = 0;
68         for(int i = 1 ; i <= N ; i++){
69             int cnt = 0;
70             for(int j = 1 ; j <= N ; j++)
71                 if(num[i][2] != num[j][2]){
72                     now[++cnt].l = calcK(num[i][0] , num[i][2] , num[j][0] , num[j][2]);
73                     now[cnt].r = calcK(num[i][0] , num[i][2] , num[j][1] , num[j][2]);
74                     now[cnt].ind = j;
75                     if(now[cnt].l > now[cnt].r)
76                         swap(now[cnt].l , now[cnt].r);
77                     now[cnt].l -= eps;
78                     now[cnt].r += eps;
79                     ano[cnt] = now[cnt];
80                 }
81             sort(now + 1 , now + cnt + 1 , cmp1);
82             sort(ano + 1 , ano + cnt + 1 , cmp2);
83             int p = 1;
84             long long ans = num[i][1] - num[i][0];
85             maxN = max(maxN , ans);
86             for(int j = 1 ; j <= cnt ; j++){
87                 ans += num[now[j].ind][1] - num[now[j].ind][0];
88                 while(ano[p].r < now[j].l){
89                     ans -= num[ano[p].ind][1] - num[ano[p].ind][0];
90                     ++p;
91                 }
92                 maxN = max(maxN , ans);
93             }
94         }
95         printf("%lld\n" , maxN);
96     }
97     return 0;
98 }

猜你喜欢

转载自www.cnblogs.com/Itst/p/9904690.html