题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5714
题目大意:给你n只小船,给你每个船的左端点和右端点,以及航行方向(左,右)和到岸边的距离,每个船的航行距离一样。如果有个人在岸边且只能垂直于岸边拍摄(与岸边呈45度角),问最多可以拍到多少只完整的小船。
思路:
1、首先,因为所有船只航速一样,所以同一方向的船只相对静止。
2、如果在l点可以拍摄到向右航行的船只数目为num1,在r点可以拍摄到向左航行的船只数量为num2,l<=r,那么一定存在某一时刻拍摄到的船只数量等于num1+num2.这个很好证明,两个同样的三角形相向而行,必定会重合。
3、如果有y-x>2z,则该船不可能出现在镜头中。如果y-x<=2z,那么该船可被拍摄的范围是(y-z,x+z),画个图就理解了。但是做的时候把范围搞成了(x+z , y-z)。。。显然错了
4、我们可以处理出所有船只可被拍摄的范围,然后求区间内线段个数最多的点。方法是左端点标记为1,右端点标记为-1,排序后从左到右扫描一遍,遇到1++,遇到-1--;
5、不同方向的船只分开处理。
复杂度nlogn。
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <iomanip>
using namespace std;
//#pragma comment(linker, "/STACK:102400000,102400000")
#define maxn 1000005
#define MOD 1000000007
#define mem(a , b) memset(a , b , sizeof(a))
#define LL long long
#define ULL unsigned long long
const long long INF=0x3fffffff;
int n , t;
struct node
{
int val , flag ,d;
} a[maxn] , b[maxn];
int ans[maxn];
bool cmp(node n1,node n2)
{
if(n1.val != n2.val) return n1.val < n2.val;
else if(n1.flag != n2.flag) return n1.flag > n2.flag;
else return n1.d < n2.d;
}
int main()
{
scanf("%d" , &t);
int cas = 0;
while(t--)
{
mem(ans , 0);
scanf("%d" , &n);
int x , y , z , d;
int id1 = 0 , id2 = 0;
for(int i = 0 ; i < n ; i ++)
{
scanf("%d %d %d %d" , &x , &y , &z , &d);
x += 1000000;
y += 1000000;
if((y - x) > 2*z) continue;
a[id1].val = x + z;
a[id1].d = d;
a[id1++].flag = -1;
a[id1].val = y - z;
a[id1].d = d;
a[id1++].flag = 1;
}
sort(a , a + id1 , cmp);
int ans1 = 0 ;
int num = 0;
for(int i = id1 - 1; i >= 0 ; i --)
{
if (a[i].d < 0 && a[i].flag == -1) num++;
ans[i] = max(ans[i + 1], num);
if (a[i].d < 0 && a[i].flag == 1) num--;
}
num = 0;
for(int i = 0 ; i < id1 ; i ++)
{
if(a[i].d > 0)
{
if(a[i].flag == 1) num++;
ans1 = max(ans1 , num + ans[i]);
if(a[i].flag == -1) num--;
}
}
printf("Case #%d:\n%d\n", ++cas, ans1);
}
return 0;
}