BZOJ4614 [Wf2016]Oil

Posted ljh2000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ4614 [Wf2016]Oil相关的知识,希望对你有一定的参考价值。

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

 

 

 

本文作者:ljh2000 

作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

 

 

Description

给定n条平行线段,每条线段的价值是它的长度。现在用一条直线贯穿最大价值的线段,求最大的价值。
N<=2000。
 

Input

第一行一个数n表示线段数。家下来n行每行三个数x0,x1和y,表示线段(x0,y)-(x1,y)。
|x0|,|x1|<=10^6,1<=y<=10^6。线段无交。

Output

输出最大的价值。

Sample Input

1
-100 180 20

Sample Output

280

 

正解:结论+搜索

解题报告:

  这道题很有意思,我开始也没想到那个结论。

  这道题有一个很有用的结论:最优解的直线必然是经过了某一条线段的端点。仔细想想就会发现其实很有道理,或者说显然?

  这样就很方便了,因为$n$只有$2000$,所以 $n^2$ 暴力即可。我不妨枚举一个端点,作为直线必然经过的那个端点,那么对于经过这个端点的直线,如果我想经过别的线段,显然可以通过作出别的线段的两个端点到这个点的斜率来得到一个可行的范围。假如直线斜率在这个范围内,这条线段就会产生贡献。我们得到了一个可行的做法:得到所有线段关于这个点的斜率得到若干个区间,之后扫一遍即可知道在什么时候能取到最大值了。但是有一些需要注意的地方:斜率有可能不存在,而且可能接近无限大。考虑到直线不能平行于线段,即斜率不能为$0$,我们不妨用斜率的倒数来维护上述操作,很容易发现,对于这道题来说,会减少很多计算而且方便很多,精度误差也小很多。

 

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 2011;
const double eps = 1e-8;
int n,ans,tot;
struct node{int x0,x1,y,len;}a[MAXN];
struct seq{double k;int type;}b[MAXN*2];
inline bool cmp(seq q,seq qq){ return q.k<qq.k; }
inline int getint(){
    int w=0,q=0; char c=getchar(); while((c<\'0\'||c>\'9\') && c!=\'-\') c=getchar();
    if(c==\'-\') q=1,c=getchar(); while (c>=\'0\'&&c<=\'9\') w=w*10+c-\'0\',c=getchar(); return q?-w:w;
}

inline void solve(double x,double y){
	int cnt=0;
	for(int i=1;i<=n;i++) {
		if(a[i].y==y) continue;
		b[++cnt].k=(a[i].x0-x)/(a[i].y-y); 
		b[++cnt].k=(a[i].x1-x)/(a[i].y-y);
		if(b[cnt].k>b[cnt-1].k) {
			b[cnt-1].type=a[i].len;
			b[cnt].type=-a[i].len;
			b[cnt].k+=eps;
		}
		else{
			b[cnt-1].type=-a[i].len;
			b[cnt].type=a[i].len;
			b[cnt-1].k+=eps;
		}
	}
	sort(b+1,b+cnt+1,cmp);
	for(int i=1;i<=cnt;i++) {
		tot+=b[i].type;
		if(tot>ans) ans=tot;
	}
}

inline void work(){
	n=getint(); ans=0;
	for(int i=1;i<=n;i++) {
		a[i].x0=getint(),a[i].x1=getint(),a[i].y=getint();
		if(a[i].x0>a[i].x1) swap(a[i].x0,a[i].x1);
		a[i].len=a[i].x1-a[i].x0;
	}
	for(int i=1;i<=n;i++) {
		tot=a[i].len; if(tot>ans) ans=tot;
		solve(a[i].x0,a[i].y);
		tot=a[i].len;
		solve(a[i].x1,a[i].y);
	}
	printf("%d",ans);
}

int main()
{
    work();
    return 0;
}

  

以上是关于BZOJ4614 [Wf2016]Oil的主要内容,如果未能解决你的问题,请参考以下文章

4609: [Wf2016]Branch Assignment 最短路 DP (阅读理解题)

[bzoj1177]Oil

BZOJ 1177 Oil(特技枚举)

bzoj1177 [Apio2009]Oil

BZOJ 4078: [Wf2014]Metal Processing Plant

BZOJ3963[WF2011]MachineWorks cdq分治+斜率优化