来更新一篇blog吧
最近做了一下hackerrank的20/20的比赛。平时都只能过2题,这周顺利地通过了四道题目竟然。当妄图冲击衬衫的时候,发现剩下三个题一点招数都没有,之后就跑去看了一下node.js了。。。
这次比赛的第四个题目是用cpp敲的,之前都是偷懒用python。
Bike Racers
【题意描述】
在城市里有n个骑车的人,有m辆自行车,人可以同时沿着任意方向行走,每单位时间移动单位距离。要求出前k个到达自行车的人所花费的最小时间。人和自行车都用整数坐标表示。
1 <= N <= 250
1 <= M <= 250
1 <= K <= min(N,M)
0 <= xi,yi <= 107
【思路】
这一个题目,拿到之后没什么想法。然后感觉应该像是二分图的匹配问题。然后就去恶补了二分图,刷了三四道题目。第二天晚上回来,发现确实是二分图……那么大意就是求出这个图的一个子图,这个子图的二分图匹配数为k并且匹配的边中最大值最小。
那么这道题可以用类似克鲁斯卡尔最小生成树的问题来解决。将边从小到大进行排序,依次添加到图中。每次添加检测匹配数,当匹配数为k的时候,最后添加的那条边就是所求解。匹配当然是采用匈牙利算法进行。
算法确定,之后就是数据结构的问题:
如何保存图是本题的关键。
假设边以经按照权值排序完成。加边只修要O(1)的时间。
题目的总体复杂度为V*匈牙利算法的复杂度(V为边数)
匈牙利算法的复杂度为DFS的复杂度V*E(边数*点数)
通常的题目中,使用邻接矩阵来保存图,匈牙利算法就退化成为了E*E*E(V=E*E),于是总体复杂度就变成了E^5。必须会TLE。
世界就是如此美好,这个时候如果用边表来保存整个图,那么每次匈牙利算法的复杂度仍旧是V*E,总体复杂度就变为了(E*1+E*2+……+E*E*E)=1/2*(E^3)*(E+1)大约为E^4。看上去好很多了。
排序的优化:
为了让算法看上去更快一点,我又对排序这个地方产生了想法,如果排序的话,排序的复杂度为:
V*logV = E*E*log(E*E) = 2E*ElogE。在最坏的情况下,加入了最后一条边才能出结果,这个时候复杂度是不会有改善的。这里考虑平均情况,也就是说不需要每次都用到所有的边,这个时候使用小顶堆可以胜任!
以下是拙劣的代码。窃喜一下,还是觉得有长进的…
1: #include <iostream>
2: #include <cstring>
3: #include <algorithm>
4: #include <queue>
5:
6: using namespace std;
7:
8: struct Point {
9: long long x,y;
10: Point( long long _x, long long _y ) {
11: x = _x;
12: y = _y;
13: }
14: Point() {}
15: long long dis( Point p ) {
16: long long lx = x - p.x;
17: long long ly = y - p.y;
18: lx *= lx;
19: ly *= ly;
20: return lx+ly;
21: }
22: };
23:
24: struct Edge {
25: int y,next;
26: long long l;
27: Edge ( int _y, long long _l, int _next ) {
28: y = _y;
29: next = _next;
30: l = _l;
31: }
32: Edge () {}
33: };
34:
35: struct E{
36: int x,y;
37: long long l;
38: E( int _x, int _y, long long _l ) {
39: x = _x;
40: y = _y;
41: l = _l;
42: }
43: E(){}
44: bool operator<( const E &e ) const {
45: return l > e.l;
46: }
47: };
48:
49: const int MAXSIZE = 255;
50: const int NC = MAXSIZE*2;
51:
52: priority_queue<E> Q;
53:
54: int head[NC];
55: Edge e[MAXSIZE*MAXSIZE];
56: Point p[MAXSIZE];
57: int visited[NC];
58: int mark[NC];
59:
60:
61: int e_len;
62: int n,m,k;
63: long long last;
64:
65: void add( E ex ) {
66: e[e_len] = Edge(ex.y,ex.l,head[ex.x]);
67: head[ex.x] = e_len++;
68: }
69:
70: void input() {
71: while ( !Q.empty() ) {
72: Q.pop();
73: }
74: memset(e,-1,sizeof(e));
75: memset(head,-1,sizeof(head));
76: memset(p,0,sizeof(p));
77: e_len = 0;
78: cin>>n>>m>>k;
79: for ( int i = 0; i < n; i++ ) {
80: long long x,y;
81: cin>>x>>y;
82: p[i] = Point(x,y);
83: }
84: for ( int i = 0; i < m; i++ ) {
85: long long x,y;
86: cin>>x>>y;
87: Point t(x,y);
88: for ( int j = 0; j < n; j++ ) {
89: Q.push(E(j,MAXSIZE+i,t.dis(p[j])));
90: }
91: }
92: }
93:
94: int DFS( int x ) {
95: for ( int i = head[x]; i != -1; i = e[i].next ) {
96: int y = e[i].y;
97: if ( !visited[y] ) {
98: visited[y] = 1;
99: if ( ( -1 == mark[y] ) || ( DFS(mark[y]) ) ) {
100: mark[y] = x;
101: return 1;
102: }
103: }
104: }
105: return 0;
106: }
107:
108: bool test() {
109: memset(mark,-1,sizeof(mark));
110: int result = 0;
111: for ( int i = 0; i < n; i++ ) {
112: memset(visited,0,sizeof(visited));
113: result += DFS(i);
114: }
115: if ( result == k ) {
116: return true;
117: } else {
118: return false;
119: }
120: }
121:
122: void solve () {
123: for ( int i = 0; i < k; i++ ) {
124: E ex = Q.top();
125: Q.pop();
126: add(ex);
127: last = ex.l;
128: }
129: while ( !test() ) {
130: E ex = Q.top();
131: Q.pop();
132: add(ex);
133: last = ex.l;
134: }
135: cout<<last<<endl;
136: }
137:
138: int main() {
139: input();
140: solve();
141: }