蓝桥杯国赛训练第一周
P2324 [SCOI2005] 骑士精神 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
主要在于 $A*$ 函数中估价函数,这里给出最好想也是我想出来的一种方法,也就是当黑白棋子各自都在对方的领域上,那么就可以考虑一种最小的消耗情况,也就是走一步顶两步,也就是黑白互换,那么此时所需要消耗的最小步数就是不符合棋子的最大值
#include <bits/stdc++.h> #define int long long using namespace std; const int N = 1e6 + 10, mod = 1e9 + 7; char mp[10][10]; int dx[] = {1, 1, -1, -1, 2, 2, -2, -2}, dy[] = {2, -2, 2, -2, 1, -1, 1, -1}; int opposite[8] = {3, 2, 1, 0, 7, 6, 5, 4}; //操作数的相反数,具体作用在下 int f() { int black = 0, white = 0; for (int i = 1; i <= 5; i++) if (mp[1][i] != '1') black++; for (int i = 2; i <= 5; i++) if (mp[2][i] != '1') black++; for (int i = 4; i <= 5; i++) if (mp[3][i] != '1') black++; if (mp[4][5] != '1') black++; if (mp[2][1] != '0') white++; for (int i = 1; i <= 2; i++) if (mp[3][i] != '0') white++; for (int i = 1; i <= 4; i++) if (mp[4][i] != '0') white++; for (int i = 1; i <= 5; i++) if (mp[5][i] != '0') white++; return max(black, white); } bool dfs(int now, int maxdep, int x, int y, int lst) //当前的步数,最大允许走的步数,坐标,上一步的操作 { int cnt = f(); if (now + f() > maxdep) return false; if (!cnt) return true; for (int i = 0; i < 8; i++) { int xx = x + dx[i], yy = y + dy[i]; if (xx < 1 || xx > 5 || yy < 1 || yy > 5 || opposite[i] == lst) //如果我们上一步走的↑,很明显的这一步不能走↓,opposite就是干这个事情的 continue; mp[x][y] = mp[xx][yy], mp[xx][yy] = '*'; if (dfs(now + 1, maxdep, xx, yy, i)) return true; mp[xx][yy] = mp[x][y], mp[x][y] = '*'; } return false; } void solve() { int x, y; for (int i = 1; i <= 5; i++) for (int j = 1; j <= 5; j++) { cin >> mp[i][j]; if (mp[i][j] == '*') x = i, y = j; } int dep = 0; while (dep <= 15 && !dfs(0, dep, x, y, -1)) ++dep; cout << (dep <= 15 ? dep : -1) << '\n'; } signed main() { std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int t; cin >> t; while (t--) solve(); }
P1491 集合位置 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意明了,也就是求出非严格的次短路即可,那么我们首先跑一遍最短路并储存最短路径
考虑一条最短路径上的边,可以肯定的是若最短路径中的一条边消失,其它边对终点的贡献仍然是最小的,所以说我们只需要删除一个最短路径上的边然后继续跑最短路就可以了
正确性: 对于一个最短路径,图中其它路径必然不存在更优的路径,若删去最短路径的一条边之后,最短路径一定会改变,此时再次求一遍最短路径,那么这个路径一定不劣于其他路径,且一定不优于不去除边的最短路径,故为次短路径
#include <bits/stdc++.h> #define int long long using namespace std; const int N = 1e6 + 10, mod = 1e9 + 7; vector<pair<int, int>> point(N); vector<pair<int, double>> g[N]; vector<int> pre(N); vector<double> dijkstra(int n, int aa, int bb) { vector<double> dist(n + 1, 1e9); vector<bool> vis(n + 1, false); priority_queue<pair<double, int>, vector<pair<double, int>>, greater<pair<double, int>>> que; que.push({0, 1}), dist[1] = 0; while (!que.empty()) { auto now = que.top(); que.pop(); int x = now.second; double y = now.first; if (vis[x]) continue; vis[x] = true; for (auto u : g[x]) { int v = u.first; double w = u.second; if ((x == aa && v == bb) || (x == bb && v == aa)) continue; if (dist[v] > y + w) { dist[v] = y + w, que.push({dist[v], v}); if (aa == -1) pre[v] = x; } } } return dist; } signed main() { std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int n, m, a, b; cin >> n >> m; cin >> a >> b; point[1] = {a, b}; for (int i = 2; i <= n - 1; i++) { cin >> a >> b; point[i] = {a, b}; } vector<vector<double>> dis(n + 1, vector<double>(n + 1)); cin >> a >> b, point[n] = {a, b}; auto get_distance = [](pair<int, int> a, pair<int, int> b) { int x = a.first, y = a.second, xx = b.first, yy = b.second; return (double)(sqrt((x - xx) * (x - xx) + (y - yy) * (y - yy))); }; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) dis[i][j] = get_distance(point[i], point[j]); for (int i = 1; i <= m; i++) { cin >> a >> b; double c = dis[a][b]; g[a].push_back({b, c}), g[b].push_back({a, c}); } vector<double> ans = dijkstra(n, -1, -1); double res = 1e9; for (int i = n; i != 1; i = pre[i]) //删除每一条最短路径 { int aa = i, bb = pre[i]; ans = dijkstra(n, aa, bb); res = min(res, ans[n]); } if (res == 1e9) cout << "-1" << '\n'; else printf("%.2lf\n", res); return 0; }
P2149 [SDOI2009] Elaxia的路线 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
>题解太过繁琐复杂,又是4遍dijkstra和2遍拓扑排序,又是最短路DAG什么的,我稍微看了下题解,大多数都是要跑四个最短路
这里提供只跑两次最短路,个人认为比较好理解的一种解法:
首先了解题意,求出两条最短路的公共路径,那么很明显公共路径的权值和相加就是答案
$Tips1: $最短路不止有一条,如何求出所有的最短路? 很简单,考虑求一条最短路径,我们要使用一个数组来记录它的前驱节点,如果是多条最短路的话,那么前驱节点就不止有一个,所以很明显可以开一个二维数组来记录最后跑一遍 $dfs$ 即可
$Tips2:$ 如何求出答案? 在 $dfs$ 的过程中用 $dp$ 维护每一条最短路径的最大贡献即可,最后求出所有点中的最大贡献就是答案 $dp$ 表示的是从某个节点开始走的最大贡献值
注意: 在遍历的时候要剪枝一下,统计过的点不需要再统计了,不然会T
#include <bits/stdc++.h> using namespace std; const int N = 2040, mod = 1e9 + 7; typedef pair<int, int> pii; int w[N][N]; bool check[N][N], vis[N]; vector<int> dp(N); //dp[u]就是从u点出发可以得到的最大贡献 vector<pair<int, int>> g[N]; vector<vector<int>> dijkstra(int n, int s) { vector<double> dist(n + 10, 1e9); vector<bool> vis(n + 10, false); vector<vector<int>> pre(n + 10, vector<int>(0)); priority_queue<pii, vector<pii>, greater<pii>> que; que.push({0, s}), dist[s] = 0; while (!que.empty()) { auto now = que.top(); que.pop(); int x = now.second, y = now.first; if (vis[x]) continue; vis[x] = true; for (auto u : g[x]) { int v = u.first, w = u.second; if (dist[v] > y + w) { dist[v] = y + w, que.push({dist[v], v}); pre[v] = {x}; } else if (dist[v] == y + w) pre[v].push_back(x), que.push({dist[v], v}); } } return pre; } void dfs(int ed, int u, vector<vector<int>> &pre, bool flag) { if (u == ed || vis[u]) return; vis[u] = true; for (auto x : pre[u]) { dfs(ed, x, pre, flag); if (flag && check[x][u]) //如果是同一条路,进行统计 dp[u] = max(dp[u], dp[x] + w[x][u]); else if (!flag) check[x][u] = check[u][x] = true; } } signed main() { std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int n, m; cin >> n >> m; int x1, x2, y1, y2; cin >> x1 >> y1 >> x2 >> y2; for (int i = 1; i <= m; i++) { int a, b, c; cin >> a >> b >> c; g[a].push_back({b, c}), g[b].push_back({a, c}); w[a][b] = w[b][a] = c; } vector<vector<int>> epre = dijkstra(n, x1); vector<vector<int>> wpre = dijkstra(n, x2); // cout<<"------1------"<<'\n'; dfs(x1, y1, epre, false); //先把e同学的所有最短路都统计出来,不进行计算答案,所以设为false for (int i = 0; i <= n + 10; i++) vis[i] = false; // cout<<"------2------"<<'\n'; dfs(x2, y2, wpre, true); //找相同的路径,并计算贡献,设为true int res = 0; for (int i = 1; i <= n; i++) res = max(res, dp[i]); cout << res << '\n'; return 0; }
P1967 [NOIP2013 提高组] 货车运输 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
$Kruskal$重构树模板题,我之前的文章有讲,需要的可自取,这里不再赘述
#include <bits/stdc++.h> #define int long long using namespace std; const int N=1e6+10,mod=1e9+7; int sz[N],dep[N],top[N],son[N],fa[N],w[N],p[N]; vector<int>g[N]; struct node{ int u,v,w; bool operator<(const node&W)const{ return w>W.w; } }edge[N]; int find(int x){ if(x!=p[x]) p[x]=find(p[x]); return p[x]; } void dfs1(int u){ sz[u]=1,dep[u]=dep[fa[u]]+1; for(auto x:g[u]){ if(x==fa[u]) continue; fa[x]=u; dfs1(x); sz[u]+=sz[x]; if(sz[x]>sz[son[u]]) son[u]=x; } } void dfs2(int u,int h){ top[u]=h; if(son[u]) dfs2(son[u],h); for(auto x:g[u]){ if(x==fa[u]||x==son[u]) continue; dfs2(x,x); } } int lca(int a,int b){ while(top[a]!=top[b]){ if(dep[top[a]]>dep[top[b]]) a=fa[top[a]]; else b=fa[top[b]]; } return dep[a]>dep[b]?b:a; } signed main() { std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); int n,m; cin>>n>>m; for(int i=1;i<=m;i++){ int a,b,c; cin>>a>>b>>c; edge[i]={a,b,c}; } sort(edge+1,edge+1+m); for(int i=1;i<=4*n;i++) p[i]=i; int cnt=n; for(int i=1;i<=m;i++){ int u=find(edge[i].u),v=find(edge[i].v); if(u==v) continue; cnt++,p[u]=p[v]=find(cnt); w[cnt]=edge[i].w; g[cnt].push_back(u),g[cnt].push_back(v); } for(int i=cnt;i>=n+1;i--) if(!son[i]||!top[i]) dfs1(i),dfs2(i,i); int q; cin>>q; while(q--){ int a,b; cin>>a>>b; int res=w[lca(a,b)]; cout<<(res?res:-1)<<'\n'; } return 0; }