1066F. Yet another 2D Walking
time limit per test: 4 seconds
memory limit per test: 256 megabytes
URL
http://codeforces.com/contest/1066/problem/F
Introduction
贪心 + 简化极角排序 + DP
Input
Codeforces老规矩,一组输入。第一行是点的个数,接下来V行是每个点的 x y 坐标
要从原点出发,并且按层数非递减顺序访问每个点(点A(x, y)的层数是max(x, y))
求最短路径长度和(距离是竖直/水平折线的距离)
Output
输出最短距离和
Sample Input
8
2 2
1 4
2 3
3 1
3 4
1 1
4 3
1 2
Sample Output
15
图解:
Analysis & AC Code
分析大致如下:
-
1. 贪心: 按层走,在每一层内部走的时候,最短的路一定是从最左上的点出发然后沿顺时针方向到达最右下的点期间不回头,或者是恰好完全相反,严格按逆时针走
-
2. 简化版极角排序(因为只需要把每一层给排序就行,所以x小的或者x相等但y大的一定在左方/上方,如果选定顺时针的话就是在上游),然后以此排序,再顺序遍历,就可以求出每两个相邻点直接的距离了,再求和就是每一层内的最短路径和
-
3. DP,每一层都可能以顺时针方向通过,或者以逆时针方向通过,并且还跟上一层是顺时针通过还是逆时针通过有关(决定着状态转移时层间距是多少),很自然的就可以想到二维DP,第一维就是层数,第二维表示这一层是顺时针通过的还是逆时针通过的,然后最后取min{dp[MAX_LAWYER][R], dp[MAX_LAWYER][S]}就是答案
代码:
#include <cstdio>
#include <set>
#include <map>
#include <vector>
#include <algorithm>
#define cincout cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
#define _SN(x) do{char _c=getchar(),_v=1;for(x=0;_c<48||_c>57;_c=getchar())if(_c==45)_v=-1;for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+(_c^48),_c=getchar());x*=_v;}while(0)
#define _SAN(a,n) do{int _i=0,_n=n;for(;_i<_n;++_i)_SN(a[_i]);}while(0)
#define _SA(a,l,r) do{int _i=l,_r=r;for(;_i<_r;++_i)_SN(a[_i]);}while(0)
#define _gS(_1,_2,_3,_sc, ...) _sc
#define sc(...) _gS(__VA_ARGS__,_SA,_SAN,_SN, ...)(__VA_ARGS__)
#define _F0N(i,n) for(i=0;i<n;++i)
#define _FLR(i,l,r) for(i=l;i<r;++i)
#define _gF(_1,_2,_3,_F, ...) _F
#define F(...) _gF(__VA_ARGS__,_FLR,_F0N, ...)(__VA_ARGS__)
#define _FD0(i,n) for(i=0;i<=n;++i)
#define _FDL(i,l,r) for(i=l;i<=r;++i)
#define _gFD(_1,_2,_3,_FD, ...) _FD
#define FD(...) _gFD(__VA_ARGS__,_FDL,_FD0, ...)(__VA_ARGS__)
#define ALL(X) (X).begin(),(X).end()
#define cs_(s,a,n) do{int _i=0;scanf("%*[^a-zA-Z]");while(scanf("%[^ ,.?!\n]%*[ ,.?!]",s)==1)strcpy(a[_i++],s);n=_i;}while(0)
#define PC(c) putchar(c)
#define PB push_back
#define MP make_pair
using namespace std;
#define DIST(p1,p2) (p1.x>p2.x?p1.x-p2.x:p2.x-p1.x)+(p1.y>p2.y?p1.y-p2.y:p2.y-p1.y)
#define MAX(a,b) a>b?a:b
#define MIN(a,b) a<b?a:b
void PRT(long long a){if(a<0)putchar(45),a=-a;if(a>=10)PRT(a/10);putchar(a%10+48);}
constexpr int MV(250000);
struct Point
{
int x, y;
Point(void) { }
Point(int x, int y) : x(x), y(y) { }
inline bool operator < (const Point &o) const
{
return y>o.y || y==o.y&&x<o.x;// 顺时针
}
} arr[MV];
enum
{
R, S, RSCNT
};
map<int, int> mp;int tot;
vector<Point> points[MV];
long long dist_sum[MV];
long long dp[MV][RSCNT];
int main()
{
// cincout;
Point p1, p2;
int n;sc(n);
register int tpx,tpy;
for(int i=0; i<n; i++)
{
sc(tpx);sc(tpy);
arr[i].x=tpx,arr[i].y=tpy;
int c = MAX(tpx,tpy);
mp.insert(MP(c,1));
}
for(auto & pr : mp)
{
pr.second=tot++;
}
for(int i=0; i<n; i++)
{
int mx_p = MAX(arr[i].x,arr[i].y);
points[mp[mx_p]].PB(arr[i]);
}
for(auto & pr : mp)
{
int o = pr.second;
std::sort(points[o].begin(),points[o].end());
dist_sum[o]=0;
for(int j=1,SZ=points[o].size(); j<SZ; j++)
dist_sum[o] += DIST(points[o][j], points[o][j-1]); // 第o层的距离和
}
Point ZERO(0,0);
dp[0][R] = dist_sum[0] + DIST(points[0].front(),ZERO);
dp[0][S] = dist_sum[0] + DIST(points[0].back(),ZERO);
for(int i=1; i<tot; i++)
{
dp[i][R] = dp[i-1][R]+DIST(points[i].front(),points[i-1].back());
long long tp=dp[i-1][S]+DIST(points[i].front(),points[i-1].front());
if (dp[i][R] > tp)
dp[i][R] = tp;
dp[i][R] += dist_sum[i];
dp[i][S] = dp[i-1][R]+DIST(points[i].back(),points[i-1].back());
tp = dp[i-1][S]+DIST(points[i].back(),points[i-1].front());
if (dp[i][S] > tp)
dp[i][S] = tp;
dp[i][S] += dist_sum[i];
}
PRT(MIN(dp[tot-1][R], dp[tot-1][S]));
return 0;
}
(当时打比赛的时候这道题是1:59:54 AC 的qwq)