2017-2018 ACM-ICPC German Collegiate Programming Contest (GCPC 2017) Solution
A. Drawing Borders
Unsolved.
B. Buildings
Unsolved.
C. Joyride
Upsolved.
题意:
在游乐园中,有n个游玩设施,有些设施之间有道路,给出每个设施需要花费的时间和价格,以及经过每条道路的时间,求从设施1出发恰好回到设施1恰好花费时间x的最小花费。
思路:
$dist[i][j] 表示 第i个时刻 到第j个点的最小花费 从<t[1], 1> -> <x, 1>$
跑最短路即可
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 3010 5 #define ll long long 6 #define INF 0x3f3f3f3f3f3f3f3f 7 int X, n, m, T, t[N], p[N]; 8 vector <int> G[N]; 9 10 struct node 11 { 12 int x, to; ll w; 13 node () {} 14 node (int x, int to, ll w) : x(x), to(to), w(w) {} 15 bool operator < (const node &r) const { return w > r.w; } 16 }; 17 18 bool used[N][N]; 19 ll dist[N][N]; 20 void Dijkstra() 21 { 22 for (int i = 1; i <= X; ++i) for (int j = 1; j <= n; ++j) dist[i][j] = INF, used[i][j] = 0; 23 priority_queue <node> q; q.emplace(t[1], 1, 0); dist[t[1]][1] = p[1]; 24 while (!q.empty()) 25 { 26 int x = q.top().x, u = q.top().to; q.pop(); 27 if (x > X) continue; 28 if (used[x][u]) continue; 29 used[x][u] = 1; 30 if (!used[x + t[u]][u] && dist[x + t[u]][u] > dist[x][u] + p[u]) 31 { 32 dist[x + t[u]][u] = dist[x][u] + p[u]; 33 q.emplace(x + t[u], u, dist[x + t[u]][u]); 34 } 35 for (auto v : G[u]) if (!used[x + t[v] + T][v] && dist[x + t[v] + T][v] > dist[x][u] + p[v]) 36 { 37 dist[x + t[v] + T][v] = dist[x][u] + p[v]; 38 q.emplace(x + t[v] + T, v, dist[x + t[v] + T][v]); 39 } 40 } 41 } 42 43 int main() 44 { 45 while (scanf("%d", &X) != EOF) 46 { 47 scanf("%d%d%d", &n, &m, &T); 48 for (int i = 1, u, v; i <= m; ++i) 49 { 50 scanf("%d%d", &u, &v); 51 G[u].push_back(v); 52 G[v].push_back(u); 53 } 54 for (int i = 1; i <= n; ++i) scanf("%d%d", t + i, p + i); 55 Dijkstra(); 56 if (dist[X][1] == INF) puts("It is a trap."); 57 else printf("%lld\n", dist[X][1]); 58 } 59 return 0; 60 }
D. Pants On Fire
Solved.
题意:
给出一些关系,再给出一些关系的询问,对于询问给出三种结果。
思路:
Floyd求闭包,但是我还是想知道为什么拓扑排序不行,难道是题意读错,它不一定是个DAG?
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 410 5 int n, m; 6 map <string, int> mp; int cnt; 7 char a[N], b[N]; 8 int G[N][N]; 9 10 int getid(char *s) 11 { 12 if (mp.find(s) == mp.end()) mp[s] = ++cnt; 13 return mp[s]; 14 } 15 16 void Floyd() 17 { 18 for (int k = 1; k <= cnt; ++k) 19 { 20 for (int i = 1; i <= cnt; ++i) 21 { 22 for (int j = 1; j <= cnt; ++j) 23 { 24 if (G[i][k] & G[k][j]) 25 G[i][j] = 1; 26 } 27 } 28 } 29 } 30 31 void init() 32 { 33 memset(G, 0, sizeof G); 34 mp.clear(); cnt = 0; 35 } 36 37 int main() 38 { 39 while (scanf("%d%d", &n, &m) != EOF) 40 { 41 init(); 42 for (int i = 1, u, v; i <= n; ++i) 43 { 44 scanf("%s are worse than %s", a, b); 45 u = getid(a), v = getid(b); 46 G[u][v] = 1; 47 } 48 Floyd(); 49 //for (auto it : mp) cout << it.first << " " << rt[it.second] << " " << deep[it.second] << endl; 50 for (int i = 1, u, v; i <= m; ++i) 51 { 52 scanf("%s are worse than %s", a, b); 53 u = getid(a), v = getid(b); 54 if (G[u][v] == 1) puts("Fact"); 55 else if (G[v][u] == 1) puts("Alternative Fact"); 56 else puts("Pants on Fire"); 57 } 58 } 59 return 0; 60 }
E. Perpetuum Mobile
Upsolved.
题意:
有n个转换器,m个转换关系,转换关系是有向边
求从哪个点出发回到起点后经过的转换大于原来的值
思路:
枚举起点跑最长路,乘法可以取个$log$, 当然也可以再取个负跑最短路也可以
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 10010 5 #define INF 0x3f3f3f3f 6 #define pid pair <int, double> 7 int n, m; 8 vector <pid> G[N]; 9 10 struct node 11 { 12 int to; double w; 13 node () {} 14 node (int to, double w) : to(to), w(w) {} 15 bool operator < (const node &r) const { return w < r.w; } 16 }; 17 18 double dist[N]; bool used[N]; 19 bool Dijkstra(int st) 20 { 21 for (int i = 1; i <= n; ++i) dist[i] = 0.0, used[i] = false; 22 priority_queue <node> q; q.emplace(st, 0); 23 while (!q.empty()) 24 { 25 int u = q.top().to; q.pop(); 26 if (used[u] && u == st && dist[u] > 0) return false; 27 if (used[u]) continue; 28 used[u] = 1; 29 for (auto it : G[u]) if (dist[it.first] < dist[u] + log(it.second)) 30 { 31 dist[it.first] = dist[u] + log(it.second); 32 q.emplace(it.first, dist[it.first]); 33 } 34 } 35 return true; 36 } 37 38 bool solve() 39 { 40 for (int i = 1; i <= n; ++i) if (Dijkstra(i) == false) 41 return false; 42 return true; 43 } 44 45 int main() 46 { 47 while (scanf("%d%d", &n, &m) != EOF) 48 { 49 for (int i = 1; i <= n; ++i) G[i].clear(); 50 double w; 51 for (int i = 1, u, v; i <= m; ++i) 52 { 53 scanf("%d%d%lf", &u, &v, &w); 54 G[u].emplace_back(v, w); 55 } 56 if (solve() == false) printf("in"); 57 puts("admissible"); 58 } 59 return 0; 60 }
F. Plug It In
Unsolved.
题意:
给出一个匹配关系,可以设置一个人和三个人匹配,求最大匹配数。
思路:
先求最大匹配,再枚举每个插座,找增广路,枚举点的时候都要复制原图备份一下
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 1510 5 int n, m, k; 6 vector <int> G[N]; 7 int linker[N], vis[N], tmp[N]; 8 9 bool DFS(int u) 10 { 11 for (auto v : G[u]) if (!vis[v]) 12 { 13 vis[v] = 1; 14 if (linker[v] == -1 || DFS(linker[v])) 15 { 16 linker[v] = u; 17 return true; 18 } 19 } 20 return false; 21 } 22 23 int main() 24 { 25 while (scanf("%d%d%d", &n, &m, &k) != EOF) 26 { 27 for (int i = 1; i <= n; ++i) G[i].clear(); 28 for (int i = 1, u, v; i <= k; ++i) 29 { 30 scanf("%d%d", &u, &v); 31 G[u].push_back(v); 32 } 33 memset(linker, -1, sizeof linker); 34 int res = 0, ans = 0; 35 for (int i = 1; i <= n; ++i) 36 { 37 memset(vis, 0, sizeof vis); 38 if (DFS(i)) ++res; 39 } 40 memcpy(tmp, linker, sizeof linker); 41 for (int i = 1; i <= n; ++i) 42 { 43 int cnt = 0; 44 for (int j = 1; j <= 2; ++j) 45 { 46 memset(vis, 0, sizeof vis); 47 if (DFS(i)) ++cnt; 48 } 49 ans = max(ans, cnt); 50 memcpy(linker, tmp, sizeof tmp); 51 } 52 printf("%d\n", res + ans); 53 } 54 return 0; 55 }
G. Water Testing
Upsolved.
题意:求一个多边形内部整点个数。
思路:
皮克定理:$S = a + \frac{b}{2} - 1 S表示多边形面积,\frac{b}{2} 表示 多边形边上的整点数 a 表示多边形内部整点数$
因为点是按顺序给出,有求面积公式$S = \frac{1}{2} \sum_{i = 0}^{i = n}|\vec{P_i} x \vec{P_i - 1}|$
再考虑 线段上的整点个数为
$令 a = abs(stx - edx), b = abs(sty - edy),整点个数为gcd(a, b) + 1$
考虑证明:
根据直线两点式有:
$y - y_1 = \frac{y_2 - y_1}{x_2 - x_1} \cdot (x - x _1)$
移项得
$y = \frac{y_2 - y_1}{x_2 - x_1} \cdot (x - x_1) + y_1$
即考虑 有多少个$x使得 (x - x_1) \equiv 0 \pmod {x_2 - x_1}$
由于 $x 在 [x1, x2] 范围内 那么 (x - x_1) 的范围即 [0, x2 - x1]$
$考虑 \frac{y_2 - y_1}{x_2 - x_1} 可以跟他们的gcd约分$
$\frac{x_2 - x_1}{ \frac {x_2 - x_1}{gcd((y_2 - y_1), (x_2 - x_1))}} = gcd((y_2 - y_1), (x_2 - x_1))$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 100010 5 #define ll long long 6 int n; 7 ll x[N], y[N]; 8 9 ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a;} 10 11 int main() 12 { 13 while (scanf("%d", &n) != EOF) 14 { 15 for (int i = 1; i <= n; ++i) 16 scanf("%lld%lld", x + i, y + i); 17 x[0] = x[n]; y[0] = y[n]; 18 ll s = 0, b = 0; 19 for (int i = 1; i <= n; ++i) 20 { 21 s += x[i - 1] * y[i] - x[i] * y[i - 1]; 22 ll A = abs(x[i] - x[i - 1]); 23 ll B = abs(y[i] - y[i - 1]); 24 b += gcd(A, B); 25 } 26 s = abs(s); 27 printf("%lld\n", (s - b + 2) >> 1); 28 } 29 return 0; 30 }
H. Ratatoskr
Unsolved.
I. Uberwatch
Water.
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 300010 5 int n, m, x[N], Max[15]; 6 7 int main() 8 { 9 while (scanf("%d%d", &n, &m) != EOF) 10 { 11 memset(Max, 0, sizeof Max); 12 for (int i = 1; i <= n; ++i) scanf("%d", x + i); 13 for (int i = m + 1, res; i <= n; ++i) 14 { 15 res = x[i] + Max[m]; 16 for (int j = m; j >= 1; --j) Max[j] = max(Max[j], Max[j - 1]); 17 Max[1] = max(Max[1], res); 18 } 19 printf("%d\n", *max_element(Max + 1, Max + 1 + m)); 20 } 21 return 0; 22 }
J. Word Clock
Unsolved.
K. You Are Fired
Water.
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 10010 5 int n, k, d; 6 struct node 7 { 8 char s[10]; int c; 9 void scan() 10 { 11 scanf("%s%d", s, &c); 12 } 13 bool operator < (const node &r) const {return c > r.c;} 14 }emp[N]; 15 16 int main() 17 { 18 while (scanf("%d%d%d", &n, &d, &k) != EOF) 19 { 20 for (int i = 1; i <= n; ++i) emp[i].scan(); 21 sort(emp + 1, emp + 1 + n); 22 int tot = 0; 23 for (int i = 1; i <= k; ++i) tot += emp[i].c; 24 if (tot < d) puts("impossible"); 25 else 26 { 27 printf("%d\n", k); 28 for (int i = 1; i <= k; ++i) printf("%s, YOU ARE FIRED!\n", emp[i].s); 29 } 30 } 31 return 0; 32 }