(AtCoder Grand Contest 019) C

来源:互联网 发布:台风莫兰蒂数据 编辑:程序博客网 时间:2024/05/21 22:31

题目链接

statememt

题目大意

一个城市,有很多横着和竖着的街道,相邻街道之间的距离为100。横着的街道最多有108条,标号为0 ~ 1081,竖着的也是。(抽象成第一象限的方格平面,0x,y1081,单位长度为100)
在某些十字路口(格点)处,有n个半径为10的喷泉,要经过喷泉时,只能从外围绕。给你起点s和终点t的坐标,问从st的最短距离是多少?(题目保证每一行、每一列最多只有一个喷泉,且起点和终点处没有喷泉,也没有重复的喷泉)

数据范围

0sx,sy,tx,ty<108
1n2000000xi,yi<108

解题思路

首先保证是曼哈顿距离,然后再来考虑喷泉的情况。
经过喷泉的方式分为两种:直穿和拐穿(顾名思义,直穿是绕喷泉走180,拐穿是走90
直穿喷泉要走10π,拐穿喷泉要走5π,相比于没有喷泉要走的距离20,很明显拐穿赚了!所以就要尽可能多地拐穿,尽可能少的拐穿。
st的连线为对角线 肯定能形成一个矩形(线段在这儿就算特殊的矩形),要拐穿肯定拐穿矩形内的喷泉。将在矩形内的点按x排序,然后在y里找一个LIS,长度记为lenlen就是能最多能拐穿的喷泉数。

一种特殊情况例外:当len==min(abs(txsx),abs(tysy))+1时,必须要直穿一个喷泉!
举个例子:起点s=(0,1),终点t=(4,3),3个喷泉,分别为(1,1),(2,2),(3,3)。拐穿记为1,直穿记为0。最短距离肯定是(0, 1) -> (1,1)0 -> (2,2)1 -> (3,3)1 -> (4, 3) 或者 (0, 1) -> (1,1)1 -> (2,2)1 -> (3,3)0 -> (4, 3),长度为740+20π



外话

比赛时想到正解之后还有近两个小时的时间。那是就只考虑到sx==sysy==ty时才会直穿,当我意识到不单只有这种情况才会直穿时,已经over了。看了题解之后,思路相同,更是不快!
之后的补题,因为平时习惯以1开头,对题目中的0 ~ 1081不是很注意, 以致于求LIS时出了点小差错(代码48行)
然后WA成狗:look


详见代码:

#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <algorithm>#include <map>#include <set>#include <queue>using namespace std;typedef long long LL;const int inf = 1 << 30;const LL INF = 1LL << 60;const int MaxN = 2e5;//const double PI = 3.1415926535897932384626;const double PI = acos(-1);LL sx, sy, tx, ty;int n, tot;double res;struct point{    LL x, y;}tmp[MaxN + 5];bool cmpx(point a, point b) {    if(a.x == b.x) return a.y < b.y;    else return a.x < b.x;}//----------------LIS---------------------int len;LL a[MaxN + 5], d[MaxN + 5];int bin_search(LL x) {    int l = 0, r = len;    int mid = 0, ans = 0;    while(l <= r) {        mid = (l + r) >> 1;        if(d[mid] >= x) ans = mid, r = mid - 1;        else l = mid + 1;    }    return ans;}void get_LIS(bool c){    len = 0;    d[len] = -INF;    //初始d[0] == 0的,如果LIS中有0,就会少算一个    if(c == 1) {        for(int i = 1; i <= tot; i++)             a[i] = tmp[i].y;    }    else {        //求下降时将数组倒过来求最长        for(int i = 1; i <= tot; i++)            a[i] = tmp[tot - i + 1].y;    }    for(int i = 1; i <= tot; i++) {        if(a[i] > d[len]) d[++len] = a[i];        else {            int pos = bin_search(a[i]);            d[pos] = a[i];        }    }}//----------------------------------------int main(){    tot = 0;    res = 0.0;    scanf("%lld %lld %lld %lld", &sx, &sy, &tx, &ty);    if(sx > tx) {        //让起点在左边        swap(sx, tx); swap(sy, ty);    }    scanf("%d", &n);    for(int i = 1; i <= n; i++) {        LL x, y;        scanf("%lld %lld", &x, &y);        //将在 起点和终点所形成的矩形 中的点给抠出来        if((min(sx, tx) <= x && x <= max(sx, tx)) && (min(sy, ty) <= y && y <= max(sy, ty)))            tmp[++tot].x = x, tmp[tot].y = y;    }    sort(tmp + 1, tmp + tot + 1, cmpx);    if(sy > ty) {        //取最长下降子序列        get_LIS(0);    }    else if(sy <= ty) {        //取最长上升子序列        get_LIS(1);    }    int num = min(abs(tx - sx), abs(ty - sy)) + 1;    if(len == num) {        //肯定会直线经过一个喷泉,其他都是拐角        res = (100.0 * (abs(tx - sx) + abs(ty - sy)) - 20.0 * (LL)len) + (LL)(len - 1) * 5 * PI + 10 * PI;    }    else {        //全是拐角经过        res = (100.0 * (abs(tx - sx) + abs(ty - sy)) - 20.0 * (LL)len) + (LL)len * 5 * PI;    }    printf("%.15lf\n", res);    return 0;}
原创粉丝点击