题目链接:https://code.google.com/codejam/contest/4224486/
Problem A. Mushroom Monster
这题题意就是,有N个时间点,每个时间点,Kaylin可以吃掉一定数量的mushroom,Bartholomew可以放入任意数量的mushroom。
现在给出N个时间点分别有多少mushroom。
问:若Kaylin每个时间点可以吃任意数量的mushroom,那为了配合每个时间点的mushroom,Kaylin最少要吃掉多少蘑菇。
问:若Kaylin已恒定速度吃mushroom(即在每个时间点间吃的数量相同,若盘子空了则暂停进食),那为了配合每个时间点的mushroom,Kaylin最少要吃掉多少蘑菇。
看懂题目就是水题,第一问,只要吃掉下一个时间点相对于当前时间点减少的mushroom数量。
第二问,只要保证吃的速度要大于等于所有时间点mushroom减少的数量,即取需求速度最大值。
代码:
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <iostream> 5 #include <numeric> 6 #include <queue> 7 using namespace std; 8 9 const int MAXN = 1010; 10 const int INF = 0x3f3f3f3f; 11 12 int a[MAXN], maxr[MAXN]; 13 int T, n; 14 15 int solve1() { 16 int res = 0; 17 for(int i = 0; i < n - 1; ++i) 18 res += max(0, a[i] - a[i + 1]); 19 return res; 20 } 21 22 int solve2() { 23 int spd = 0; 24 for(int i = 0; i < n - 1; ++i) 25 spd = max(spd, a[i] - a[i + 1]); 26 int res = 0; 27 for(int i = 0; i < n - 1; ++i) 28 res += min(a[i], spd); 29 return res; 30 } 31 32 int main() { 33 freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); 34 scanf("%d", &T); 35 for(int t = 1; t <= T; ++t) { 36 scanf("%d", &n); 37 for(int i = 0; i < n; ++i) 38 scanf("%d", &a[i]); 39 maxr[n] = 0; 40 for(int i = n - 1; i >= 0; --i) 41 maxr[i] = max(maxr[i + 1], a[i]); 42 printf("Case #%d: %d %d\n", t, solve1(), solve2()); 43 } 44 }
Problem B. Haircut
题目大意:已知有B个理发师,和B个理发师给任意一个人理发所需要的时间。现在有N个人在排队,若处于队首,会找当前不是正在理发的编号最小的理发师理发。
问:处于队列第N个的人会找第几个理发师。
思路:二分时间 time,计算在time个单位时间里,有sum人已经或正在理发,又有cnt个理发师恰好没有正在理发的人。
那么,若sum + cnt ≥ N,那么第N个人在前 time 个单位时间里,一定有机会开始理发。
如此二分可以得到第N个人开始理发的时间,再由上述的sum、cnt可以得到第N个人让第几个理发师理发。
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 typedef long long LL; 7 8 const int MAXN = 1010; 9 10 int a[MAXN]; 11 int T, n, b; 12 13 bool check(LL k) { 14 LL sum = 0, cnt = 0; 15 for(int i = 0; i < b; ++i) { 16 cnt += (k % a[i] == 0); 17 sum += (k - 1) / a[i] + 1; 18 } 19 return sum + cnt >= n; 20 } 21 22 int solve() { 23 LL l = 0, r = LL(n) * *max_element(a, a + b);//1000000;// 24 while(l < r) { 25 LL mid = (l + r) >> 1; 26 if(!check(mid)) l = mid + 1; 27 else r = mid; 28 } 29 LL sum = 0, p = 0; 30 for(int i = 0; i < b; ++i) 31 sum += (l - 1) / a[i] + 1; 32 for(int i = 0; i < b; ++i) 33 if(l % a[i] == 0 && sum + ++p == n) return i + 1; 34 return -1; 35 } 36 37 int main() { 38 freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); 39 scanf("%d", &T); 40 for(int t = 1; t <= T; ++t) { 41 scanf("%d%d", &b, &n); 42 for(int i = 0; i < b; ++i) 43 scanf("%d", &a[i]); 44 printf("Case #%d: %d\n", t, solve()); 45 } 46 }
Problem C. Logging
给N个点,分别问对于每个点来说,至少删掉多少个点,能使该点在剩下的点的凸包的边上。
思路1:因为凸包的边的性质是:对于一条边,每个点都在其同侧。那么,O(n^2)枚举所有边,O(n)枚举所有点,看要删掉那些点。复杂度O(n^3),可以跑过小数据,牛叉的电脑可以过大数据(RMB玩家与贫民玩家的区别就体现在这里了!)
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <cmath> 6 #include <vector> 7 using namespace std; 8 typedef long long LL; 9 10 const int MAXN = 3010; 11 12 struct Point { 13 int x, y, id; 14 Point() {} 15 Point(int x, int y): x(x), y(y) {} 16 void read(int i) { 17 id = i; 18 scanf("%d%d", &x, &y); 19 } 20 Point operator - (const Point &rhs) const { 21 return Point(x - rhs.x, y - rhs.y); 22 } 23 bool operator < (const Point &rhs) const { 24 if(y != rhs.y) return y < rhs.y; 25 return x < rhs.x; 26 } 27 }; 28 29 LL cross(const Point &a, const Point &b) { 30 return (LL)a.x * b.y - (LL)a.y * b.x; 31 } 32 //ret >= 0 means turn right 33 LL cross(const Point &op, const Point &sp, const Point &ed) { 34 return cross(sp - op, ed - op); 35 } 36 37 Point p[MAXN]; 38 int ans[MAXN]; 39 int n, top, T; 40 41 void update_min(int &a, int b) { 42 if(a > b) a = b; 43 } 44 45 void solve() { 46 for(int i = 0; i < n; ++i) for(int j = i + 1; j < n; ++j) { 47 int lsum = 0, rsum = 0; 48 for(int k = 0; k < n; ++k) { 49 LL t = cross(p[i], p[j], p[k]); 50 if(t > 0) lsum++; 51 if(t < 0) rsum++; 52 } 53 update_min(ans[i], min(lsum, rsum)); 54 update_min(ans[j], min(lsum, rsum)); 55 } 56 } 57 58 int main() { 59 freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); 60 scanf("%d", &T); 61 for(int t = 1; t <= T; ++t) { 62 scanf("%d", &n); 63 for(int i = 0; i < n; ++i) 64 p[i].read(i), ans[i] = n - 1; 65 solve(); 66 printf("Case #%d:\n", t); 67 for(int i = 0; i < n; ++i) 68 printf("%d\n", ans[i]); 69 } 70 }
思路2:枚举每一个点,其他点绕枚举点排序,用一条过枚举点的直线旋转,计算要删掉的最少点。复杂度O(n^2logn)。
代码:
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <iostream> 5 #include <cmath> 6 using namespace std; 7 typedef long long LL; 8 9 struct Point { 10 int x, y; 11 Point() {} 12 Point(int x, int y): x(x), y(y) {} 13 Point operator - (const Point &rhs) const { 14 return Point(x - rhs.x, y - rhs.y); 15 } 16 double ang() const { 17 return atan2(y, x); 18 } 19 bool type() const { 20 if(y != 0) return y > 0; 21 return x < 0; 22 } 23 }; 24 25 LL cross(const Point &a, const Point &b) { 26 return (LL)a.x * b.y - (LL)a.y * b.x; 27 } 28 29 const int MAXN = 3010; 30 31 Point p[MAXN], v[MAXN << 1]; 32 int n, T; 33 34 bool cmp(const Point &a, const Point &b) { 35 if(a.type() != b.type()) return a.type() < b.type(); 36 return cross(a, b) > 0; 37 } 38 39 void solve(int n) { 40 for(int i = 1; i < n; ++i) 41 v[i - 1] = p[i] - p[0]; 42 n--; 43 sort(v, v + n, cmp); 44 copy(v, v + n, v + n); 45 int res = 0; 46 for(int i = 0, j = 0; i < n; ++i) { 47 if(i == j) j++; 48 while(j < i + n && cross(v[i], v[j]) >= 0) ++j; 49 res = max(res, j - i); 50 } 51 printf("%d\n", n - res); 52 } 53 54 int main() { 55 freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); 56 scanf("%d", &T); 57 for(int t = 1; t <= T; ++t) { 58 scanf("%d", &n); 59 for(int i = 0; i < n; ++i) 60 scanf("%d%d", &p[i].x, &p[i].y); 61 printf("Case #%d:\n", t); 62 for(int i = 0; i < n; ++i) { 63 swap(p[0], p[i]); 64 solve(n); 65 swap(p[0], p[i]); 66 } 67 } 68 }