【杭电多校round3】G Interstellar Travel

题意

给定二维平面上的n个点,保证只有一个点(P_1)横坐标最小,一个点横坐标最大(P_n),在坐标轴上其余点无序且可能重合
即 y_1=y_n=0, 0=x_1<x_2,x_3,...,x_n−1<x_n
要沿着x严格递增的方式选择一些降落点,从P_1走到P_n
从i点起飞到j点降落,花费的代价是x_i×y_j−x_j×y_i ,
求花费代价最小的方案,输出字典序最小的答案

分析

这题很多人一眼看过去是斜率DP。。。原谅本辣鸡没看不出来。。。但是推式子却推不出来,Claris还是你Claris,迷惑性很强。。。
观察一下上面的代价式子,会发现是一个向量叉积的式子,两两之间选择的点求叉积,联系到三角剖分,可以知道最后的花费代价是所有点连起来的有向面积的和。
如果斜率减小,则获得负面积,所以我们要沿着斜率减小的情况走。也就是维护一个上凸包。
起点,终点,凸包上的拐点必须要选,重合的点只选字典序最小的一个,共线的点,则选择字典序最小的子序列
解决重合的点可以考虑在读入点,排序后就预处理掉
共线的点可以考虑贪心从右往左,如果当前结点序号比前一个选的共线的点还要小,则这个点也选取

(题目要是没看错的话应该可以秒嘞。。。在想复杂了的情况下,思路已经很接近了。。。

代码

#include<bits/stdc++.h>
using namespace std;
struct Point{
    long long x,y;
    int id;
    bool operator < (Point b) {return x<b.x || (x == b.x && y < b.y);}
    bool operator == (Point b){return x == b.x && y == b.y;}
    long long operator *(Point b){return x*b.y-y*b.x;}
    Point operator - (Point b){return Point{x-b.x,y-b.y,0};}
}s[200005],a[200005];
bool book[200005];
int n,m;
int solve()
{
    int m = 0,k=1;
    s[m++] = a[n];
    for(int i = n-1;i>=1;i--)
    {
	    while(m>k && (s[m-1]-s[m-2])*(a[i]-s[m-2])<0) m--;
	    s[m++] = a[i];
    }
    m--;book[0] = book[m] = 1;
    for(int i = 1;i<m;i++) 
	    if((s[i-1]-s[i])*(s[i]-s[i+1])) book[i] = 1;
    int pre;
    for(int i = 0;i<m;i++)
    	if(book[i] == 1) pre = i;
    	else{
	        if(s[i].id<s[pre].id)
		        book[i] = 1, pre = i;
    	}
    putchar('1');
    for(int i = m-1;i>=0;i--)
    {
    if(book[i]) printf(" %d",s[i].id);
    }
    putchar('\n');


}
int main()
{
    int _;scanf("%d",&_);
    while(_--)
    {
	    memset(book,0,sizeof(book));
	    scanf("%d",&n);
	    for(int i = 1;i<=n;i++) 
	        scanf("%lld%lld",&a[i].x,&a[i].y),a[i].id = i;
	    sort(a+1,a+n+1);
	    int now = 1;
	    for(int i = 2;i<=n;i++)
	    {
	        if(a[i] == a[now]){
	        	if(a[i].id < a[now].id)  a[now] = a[i];
	        }
	        else a[++now] = a[i];
	    }
	    n = now;
	    solve();
    }
}
posted @ 2018-07-31 10:02  Greenty  阅读(104)  评论(0编辑  收藏  举报