2018年全国多校算法寒假训练营练习比赛(第四场)
A: 石油采集
刚开始题目读错了,乱交了4发,然后终于读对题目,想用匈牙利算法跑2分匹配,但是比赛的时候不会跑,赛后学了一下,补了一下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N = 55*55; 4 string str[55]; 5 int cnt = 0, tot, n; 6 int dx[4] = {1,-1,0,0}; 7 int dy[4] = {0,0,1,-1}; 8 int head[N], to[N], nt[N], link[N]; 9 bool vis[N]; 10 int id(int x, int y) 11 { 12 return x*n+y; 13 } 14 void add_edge(int u, int v) 15 { 16 to[tot] = v; 17 nt[tot] = head[u]; 18 head[u] = tot++; 19 } 20 bool hun(int u) 21 { 22 for(int i = head[u]; ~i; i = nt[i]){ 23 if(!vis[to[i]]){ 24 vis[to[i]] = 1; 25 if(-1 == link[to[i]]|| hun(link[to[i]])){ 26 link[u] = to[i]; 27 link[to[i]]=u; 28 return 1; 29 } 30 } 31 } 32 return 0; 33 } 34 int main() 35 { 36 ios::sync_with_stdio(false); 37 cin.tie(0); 38 cout.tie(0); 39 int T, Case = 1; 40 cin >> T; 41 while(T--) 42 { 43 tot = 0, cnt = 0; 44 memset(head, -1, sizeof(head)); 45 memset(link, -1, sizeof(link)); 46 cin >> n; 47 for(int i = 0; i < n; i++) 48 cin >> str[i]; 49 for(int i = 0; i < n; i++){ 50 for(int j = 0; j < n; j++){ 51 if(str[i][j] == '#'){ 52 for(int k = 0; k < 4; k++){ 53 int tx = i + dx[k]; 54 int ty = j + dy[k]; 55 if(tx < 0 || tx >= n || ty < 0 || ty >= n) continue; 56 if(str[tx][ty] == '#') add_edge(id(i,j), id(tx,ty)); 57 } 58 } 59 } 60 } 61 for(int i = 0; i < n; i++){ 62 for(int j = 0; j < n; j++){ 63 if(str[i][j] == '#'){ 64 if(-1 == link[id(i,j)]){ 65 memset(vis, 0, sizeof(vis)); 66 if(hun(id(i,j))) 67 cnt++; 68 } 69 } 70 } 71 } 72 cout <<"Case " << Case++ <<": " <<cnt << endl; 73 } 74 return 0; 75 }
B: 道路建设
一道很裸的最小生成数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int pre[1000]; 4 struct Node 5 { 6 int u, v, c; 7 bool operator < (const Node & x) const 8 { 9 return c < x.c; 10 } 11 }E[10000]; 12 int n, m, Max; 13 int Find(int x) 14 { 15 if(x == pre[x]) return x; 16 return pre[x] = Find(pre[x]); 17 } 18 int main() 19 { 20 ios::sync_with_stdio(false); 21 cin.tie(0); 22 cout.tie(0); 23 while(cin >> Max >> n >> m ) 24 { 25 for(int i = 1; i <= m; i++) 26 pre[i] = i; 27 for(int i = 1; i <= n; i++) 28 { 29 cin >> E[i].u >> E[i].v >> E[i].c; 30 } 31 sort(E+1, E+1+n); 32 for(int i = 1; i <= n; i++) 33 { 34 int x = Find(E[i].u), y = Find(E[i].v); 35 if(x == y)continue; 36 Max -= E[i].c; 37 if(Max < 0) break; 38 pre[x] = y; 39 } 40 if(Max < 0) 41 cout << "No\n"; 42 else 43 { 44 int cnt = 0; 45 for(int i = 1; i <= n; i++) 46 if(Find(i) == i) cnt++; 47 if(cnt == 1) cout <<"Yes\n"; 48 else cout <<"No\n"; 49 } 50 } 51 return 0; 52 }
C:求交集
比赛的时候题目没仔细读,没注意到输入的时候2个集合就是升序的,然后偷懒用set瞎几把搞了一发,然后MLE了,最后还是写了瞎几把写了一个2分查找,勉强Ac了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int INF = 0x3f3f3f3f; 4 int a[1000000+5]; 5 int ans[1000000+5]; 6 int n, m; 7 bool check(int tmp) 8 { 9 int l = 1, r = n; 10 while(l <= r) 11 { 12 int m = r+l >> 1; 13 if(tmp == a[m]) return true; 14 if(tmp < a[m]) r = m-1; 15 else l = m+1; 16 } 17 return false; 18 } 19 int main() 20 { 21 while(~scanf("%d%d",&n,&m)) 22 { 23 int tot = 0; 24 int tmp; 25 for(int i = 1; i <= n; i++) 26 { 27 scanf("%d",&a[i]); 28 } 29 sort(a+1, a+n+1); 30 for(int i = 1; i <= m; i++) 31 { 32 scanf("%d",&tmp); 33 if(check(tmp)) ans[tot++] = tmp; 34 } 35 if(tot==0) printf("empty\n"); 36 else 37 { 38 sort(ans,ans+tot); 39 for(int i = 0; i < tot; i++) 40 printf("%d%c",ans[i]," \n"[tot==i+1]); 41 } 42 } 43 return 0; 44 }
D:小明的挖矿之旅
也是赛后补的,直接看别人的规律补的,算出到达这个'.'点传送门数和出去这个'.'的传送门数(都是无法往外走的情况下),最后进来和出去的门数取大就好了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 string str[1005]; 4 int n, m; 5 bool check1(int x, int y) 6 { 7 if(x > 0 && str[x-1][y] == '.')return true; 8 if(y > 0 && str[x][y-1] == '.')return true; 9 return false; 10 } 11 bool check2(int x, int y) 12 { 13 if(x+1 < n && str[x+1][y] == '.') return true; 14 if(y+1 < m && str[x][y+1] == '.') return true; 15 return false; 16 } 17 int main() 18 { 19 ios::sync_with_stdio(false); 20 cin.tie(0); 21 cout.tie(0); 22 while(cin >> n >> m) 23 { 24 for(int i = 0; i < n; i++) 25 cin >> str[i]; 26 int cnt = 0, tohere = 0, lefthere = 0; 27 for(int i = 0; i < n; i++) 28 { 29 for(int j = 0; j < m; j++) 30 { 31 if(str[i][j] == '.') 32 { 33 cnt++; 34 if(!check1(i,j)) tohere++; 35 if(!check2(i,j)) lefthere++; 36 } 37 } 38 } 39 if(cnt <= 1) cout << 0 << endl; 40 else cout << max(tohere, lefthere) << endl; 41 } 42 return 0; 43 44 }
E:通知小弟
赛后补的, 比赛的时候也想到了强连通缩点, 但是和A一样,赛前也只是了解这个算法,没有具体实现过,赛后补的时候不小心写错了2个字符,修改了不下20发,才修改结束(自以为是的很了解了这个算法),然后早上队长说瞎几把乱贪心就可以过了(233,但是我还是想研究一下这个Tarjan算法)。
将每个强连通图锁点, 然后找到入读为0的点,如果每个入读为0的点都能被Boss通知,那么所有人都能收到通知。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N =1100; 4 int cnt = 0, tot, n, m, rtcnt, top; 5 int head[N], to[N*250], nt[N*250]; 6 bool vis[N], ok[N]; 7 int a[N], rt[N], low[N]; 8 int dfn[N], in[N], ind[N], Stack[N]; 9 void init() 10 { 11 memset(vis, 0, sizeof(vis)); 12 memset(ind, 0, sizeof(ind)); 13 memset(ok, 0, sizeof(ok)); 14 memset(head, -1, sizeof(head)); 15 memset(low, 0, sizeof(low)); 16 memset(rt, 0, sizeof(rt)); 17 tot = 0; 18 top = 0; 19 cnt = 0; 20 rtcnt = 0; 21 } 22 void add_edge(int u, int v) 23 { 24 to[tot] = v; 25 nt[tot] = head[u]; 26 head[u] = tot++; 27 } 28 void dfs(int u) 29 { 30 low[u] = dfn[u] = ++cnt; 31 vis[u] = in[u] = 1; 32 Stack[++top] = u; 33 for(int i = head[u]; ~i; i = nt[i]) 34 { 35 int v = to[i]; 36 if(!vis[v]) 37 { 38 dfs(v); 39 low[u] = min(low[v], low[u]); 40 } 41 else if(in[v]) low[u] = min(low[v], low[u]); 42 } 43 if(low[u] == dfn[u]) 44 { 45 rtcnt++; 46 int v; 47 while(top > 0) 48 { 49 v = Stack[top--]; 50 in[v] = 0; 51 rt[v] = rtcnt; 52 if(v == u) break; 53 } 54 } 55 } 56 void tarjan() 57 { 58 for(int i = 1; i <= n; i++) 59 if(!vis[i]) dfs(i); 60 for(int i = 1; i <= n; i++) 61 { 62 for(int j = head[i]; ~j; j=nt[j]) 63 { 64 if(rt[i] != rt[to[j]]) 65 ind[rt[to[j]]]++; 66 } 67 } 68 int ret = 0; 69 for(int i = 0; i < m; i++) 70 ok[rt[a[i]]] = 1; 71 for(int i = 1; i <= rtcnt; i++) 72 { 73 if(ind[i] == 0) 74 { 75 if(ok[i]) ret++; 76 else 77 { 78 cout << -1 << endl; 79 return ; 80 } 81 } 82 } 83 cout << ret << endl; 84 return ; 85 } 86 int main() 87 { 88 ios::sync_with_stdio(false); 89 cin.tie(0); 90 cout.tie(0); 91 while(cin >> n >> m) 92 { 93 init(); 94 for(int i = 0; i < m; i++) 95 cin >> a[i]; 96 for(int i = 1; i <= n; i++) 97 { 98 int k, v; 99 cin >> k; 100 for(int j = 1; j <= k; j++) 101 { 102 cin >> v; 103 add_edge(i,v); 104 } 105 } 106 tarjan(); 107 } 108 return 0; 109 }
注意有向图,然后搜索就好了,记得每次这个人被通知过了就标记一下,免得重复搜索。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N = 2005; 4 bool vis[55]; 5 int head[55], cnt = 0,n,m; 6 struct Node 7 { 8 int to; 9 int nt; 10 }E[N]; 11 void add(int u, int v) 12 { 13 E[cnt].to = v; 14 E[cnt].nt = head[u]; 15 head[u] = cnt++; 16 } 17 void bfs() 18 { 19 queue<int> q; 20 q.push(1); 21 vis[1] = 1; 22 while(!q.empty()) 23 { 24 int tmp = q.front(); 25 q.pop(); 26 if(tmp == n) return ; 27 for(int i = head[tmp]; ~i; i = E[i].nt) 28 { 29 int v = E[i].to; 30 if(vis[v]) continue; 31 vis[v] = 1; 32 q.push(v); 33 } 34 } 35 } 36 int main() 37 { 38 ios::sync_with_stdio(false); 39 cin.tie(0); 40 cout.tie(0); 41 while(cin >> n >> m ) 42 { 43 cnt = 0; 44 int u, v; 45 memset(vis, 0, sizeof(vis)); 46 memset(head, -1, sizeof(head)); 47 for(int i = 1; i <= m; i++) 48 { 49 cin >> u >> v; 50 add(u,v); 51 } 52 bfs(); 53 if(vis[n]) cout << "Yes\n"; 54 else cout << "No\n"; 55 } 56 return 0; 57 }
G:老子的意大利炮呢
比赛的时候想着反向BFS,让李云龙去走路,写了半天MLE了。
MLE代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int INF = 0x3f3f3f3f; 4 const int N = 505; 5 string str[105]; 6 int dx[4] = {1,-1,0,0}; 7 int dy[4] = {0,0,-1,1}; 8 int vis[2][2][2][105][105]; 9 int pos[5][2]; 10 int v1, v2, v3, ans, n, m; 11 struct Node 12 { 13 int x, y, v; 14 int f1, f2, f3; 15 int t; 16 }tmp, nt; 17 void BFS() 18 { 19 tmp.x = pos[4][0], tmp.y = pos[4][1]; 20 tmp.f1 = tmp.f2 = tmp.f3 = 1; 21 tmp.v = v1 + v2 + v3 + 1; 22 tmp.t = 0; 23 queue<Node> q; 24 q.push(tmp); 25 while(!q.empty()) 26 { 27 tmp = q.front(); 28 q.pop(); 29 if(tmp.x == pos[0][0] && tmp.y == pos[0][1] && !tmp.f1 && !tmp.f2 && !tmp.f3) 30 { 31 ans = min(ans, tmp.t); 32 continue; 33 } 34 if(ans < tmp.t) continue; 35 nt.t = tmp.v + tmp.t; 36 for(int i = 0; i < 4; i++) 37 { 38 nt.x = tmp.x + dx[i]; 39 nt.y = tmp.y + dy[i]; 40 nt.v = tmp.v; 41 nt.f1 = tmp.f1, nt.f2 = tmp.f2, nt.f3 = tmp.f3; 42 if(nt.x < 0 || nt.x >= n || nt.y < 0 || nt.y >= m ) continue; 43 if(nt.x == pos[1][0] && nt.y == pos[1][1] && nt.f1) nt.f1 = 0, nt.v -= v1; 44 if(nt.x == pos[2][0] && nt.y == pos[2][1] && nt.f2) nt.f2 = 0, nt.v -= v2; 45 if(nt.x == pos[3][0] && nt.y == pos[3][1] && nt.f3) nt.f3 = 0, nt.v -= v3; 46 if(nt.f1 && nt.f2 && nt.f3 && str[nt.x][nt.y] == '#') continue; 47 if(vis[nt.f1][nt.f2][nt.f3][nt.x][nt.y] < nt.t) continue; 48 vis[nt.f1][nt.f2][nt.f3][nt.x][nt.y] = nt.t; 49 q.push(nt); 50 } 51 } 52 } 53 int main() 54 { 55 ios::sync_with_stdio(false); 56 cin.tie(0); 57 cout.tie(0); 58 while(cin >> n >> m) 59 { 60 ans = 1e9; 61 memset(vis, INF, sizeof(vis)); 62 for(int i = 0; i < n; i++) 63 cin >> str[i]; 64 for(int i = 0; i < 5; i++) 65 { 66 cin >> pos[i][0] >> pos[i][1]; 67 pos[i][0]--; pos[i][1]--; 68 } 69 cin >> v1 >> v2 >> v3; 70 BFS(); 71 cout << ans << endl; 72 } 73 return 0; 74 }
后面烦恼的时候,突然想到更好的优化,墨迹了几分钟,修改了一下,就AC了,
还是让李云龙去走,走到“最后一个零件”,然后让和尚从自己的位置最短的距离跑到另外2个零件在跑到这个零件,取最小耗时就好了。因为和尚可以带着零件FQ,不包括意大利炮,所以走到其他2个位置的距离是定下来的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int INF = 0x3f3f3f3f; 4 const int N = 505; 5 string str[105]; 6 int dx[4] = {1,-1,0,0}; 7 int dy[4] = {0,0,-1,1}; 8 int vis[105][105]; 9 int pos[5][2]; 10 int v[5], ans, n, m; 11 struct Node 12 { 13 int x, y; 14 int t; 15 }tmp, nt; 16 int dis(int u, int v) 17 { 18 return abs(pos[u][0]-pos[v][0]) + abs(pos[u][1]-pos[v][1]); 19 } 20 void BFS() 21 { 22 tmp.x = pos[4][0], tmp.y = pos[4][1]; 23 tmp.t = 0; 24 vis[tmp.x][tmp.y]; 25 queue<Node> q; 26 q.push(tmp); 27 while(!q.empty()) 28 { 29 tmp = q.front(); 30 q.pop(); 31 for(int i = 1; i <= 3; i++) 32 { 33 if(tmp.x == pos[i][0] && tmp.y == pos[i][1]) 34 { 35 int tt = tmp.t * (v[1]+v[2]+v[3]+1); 36 int l = i-1; 37 int r = i+1; 38 if(l == 0) l = 3; 39 if(r == 4) r = 1; 40 int t1 = (v[l]+v[r]+1)*dis(l,i)+ (v[r]+1)*dis(l,r) + 1*dis(r,0); 41 int t2 = (v[l]+v[r]+1)*dis(r,i)+(v[l]+1)*dis(l,r)+1*dis(l,0); 42 tt = tt+ min(t1,t2); 43 ans = min(ans,tt); 44 } 45 } 46 nt.t = tmp.t+1; 47 for(int i = 0; i < 4; i++) 48 { 49 nt.x = tmp.x + dx[i]; 50 nt.y = tmp.y + dy[i]; 51 if(nt.x < 0 || nt.x >= n || nt.y < 0 || nt.y >= m) continue; 52 if(str[nt.x][nt.y] == '#' || vis[nt.x][nt.y]) continue; 53 vis[nt.x][nt.y] = 1; 54 q.push(nt); 55 } 56 } 57 return ; 58 } 59 int main() 60 { 61 ios::sync_with_stdio(false); 62 cin.tie(0); 63 cout.tie(0); 64 while(cin >> n >> m) 65 { 66 ans = 1e9; 67 memset(vis, 0, sizeof(vis)); 68 for(int i = 0; i < n; i++) 69 cin >> str[i]; 70 for(int i = 0; i < 5; i++) 71 { 72 cin >> pos[i][0] >> pos[i][1]; 73 pos[i][0]--; pos[i][1]--; 74 } 75 cin >> v[1] >> v[2] >> v[3]; 76 BFS(); 77 cout << ans << endl; 78 } 79 return 0; 80 }
H:老子的全排列呢
全场最水的题,但是我不知道STL函数,最后手写的dfs搜索过的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 bool vis[10]; 4 int put[10]; 5 void P() 6 { 7 for(int i = 1; i <= 8; i++) 8 { 9 printf("%d%c",put[i]," \n"[i == 8]); 10 } 11 } 12 void dfs(int cnt) 13 { 14 if(cnt == 9) 15 { 16 P(); 17 return; 18 } 19 for(int i = 1; i <= 8; i++) 20 { 21 if(!vis[i]) 22 { 23 vis[i] = 1; 24 put[cnt] = i; 25 dfs(cnt+1); 26 vis[i] = 0; 27 } 28 } 29 } 30 int main() 31 { 32 ios::sync_with_stdio(false); 33 cin.tie(0); 34 cout.tie(0); 35 memset(vis, 0, sizeof(vis)); 36 dfs(1); 37 return 0; 38 }
总结,比赛前的确了解AE2道题目的算法,但是没有自己去写过,还是要多联系联系,还有就是A题题目,没有仔细读,然后就乱交了4发,下次注意。总的来说还不错,比较是这4次比赛来的最好名次,但是很多小小的细节没有注意。