寒假开始,最近一段时间开始了寒假训练。这段时间如果没有别的事情,争取每天多刷题。每天都抽点空写写题解,写写刷题收获。
POJ - 2387 Dijkstra
题意
一个无向图,起点为1,终点为N的最短路,保证有解。
一个最短路的裸题。。。直接上熟悉的Dijkstra代码,堆优化复杂度。
AC代码
//#include <bits/stdc++.h> #include <iostream> #include <iomanip> #include <bitset> #include <string> #include <set> #include <stack> #include <sstream> #include <list> //#include <array> #include <vector> #include <queue> #include <map> #include <memory> #include <iterator> #include <climits> #include <cassert> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) #define all(x) (x).begin(), (x).end() typedef long long ll; typedef unsigned int UINT; typedef unsigned long long ull; typedef pair<int, int> pdi; typedef pair<ll, int> pli; int const maxn = 2000 + 10; const int INF = 0x3f3f3f3f; const ll INFL = 0x3f3f3f3f3f3f3f3f; inline int lc(int x) {return x << 1;} inline int rc(int x) {return x << 1 | 1;} int dis[maxn]; struct node { int v, w, next; }e[maxn << 1]; int head[maxn], cnt, vis[maxn]; void init() { cnt = 0; memset(head, -1, sizeof(head)); memset(vis, 0, sizeof(vis)); } void add(int u, int v, int w) { e[cnt].v = v; e[cnt].w = w; e[cnt].next = head[u]; head[u] = cnt++; } int dijkstra(int s, int ed) { memset(dis, 0x3f, sizeof(dis)); priority_queue<pair<int, int> > q; dis[s] = 0; q.push({0, s}); while (!q.empty()) { int u = q.top().second; q.pop(); if (vis[u]) continue; vis[u] = 1; for (int i = head[u]; ~i; i = e[i].next) { int v = e[i].v, w = e[i].w; if (dis[v] > dis[u] + w) { dis[v] = dis[u] + w; q.push({-dis[v], v}); } } } return dis[ed]; } int main(void) { FAST_IO; init(); int t, n; cin >> t >> n; for (int i = 0; i < t; i++) { int u, v, w; cin >> u >> v >> w; add(u, v, w); add(v, u, w); } cout << dijkstra(1, n) << endl; return 0; }
POJ - 3660 Floyd
题意
有n个牛打架,已经知道了m条打架结果,求出可以得到具体名称牛的个数。
分析
当看到确定排名的时候,第一个想到的就是拓扑排序。虽然感觉并查集找连通个数,然后拓扑排序也可以AC。但这题在最短路专题里,就去想了想最短路的方法。
若是已知 a与b有关系,b与c有关系,则可以推出a与c有关系。例如 a//b b//c 则 a//c 。这就是一种传递的关系,离散数学上的传递闭包。
根据Floyd公式,则设表示,则可以推出闭包公式
void Floyd() { for(int k=0; k<=n; ++k) for(int i=0; i<=n; ++i) for(int j=0; j<=n; ++j) a[i][j] = a[i][j] || (a[i][k] && a[k][j]); }
而当对象确定关系的个数总和,及,则表示的结果可以确定。
AC代码
//#include <bits/stdc++.h> #include <iostream> #include <iomanip> #include <bitset> #include <string> #include <set> #include <stack> #include <sstream> #include <list> //#include <array> #include <vector> #include <queue> #include <map> #include <memory> #include <iterator> #include <climits> #include <cassert> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) #define all(x) (x).begin(), (x).end() typedef long long ll; typedef unsigned int UINT; typedef unsigned long long ull; typedef pair<int, int> pdi; typedef pair<ll, int> pli; int const maxn = 100 + 10; const int INF = 0x3f3f3f3f; const ll INFL = 0x3f3f3f3f3f3f3f3f; inline int lc(int x) {return x << 1;} inline int rc(int x) {return x << 1 | 1;} int a[maxn][maxn]; int main(void) { FAST_IO; int n, m; cin >> n >> m; for (int i = 1; i <= m; i++) { int u, v; cin >> u >> v; a[u][v] = 1; } // 求传递闭包 for (int k = 1; k <= n; k++) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (a[i][k] && a[k][j]) { a[i][j] = 1; } } } } int ans = 0; for (int i = 1; i <= n; i++) { int temp = 0; for (int j = 1; j <= n; j++) { if (a[i][j] || a[j][i]) temp++; } // 若确定关系个数为n-1个,则排名确定 if (temp == n - 1) ans++; } cout << ans << endl; return 0; }
HDU - 2612 两次BFS
题意
Y和M要去同一家KFC见面。要去求出Y和M到KFC最少的总时间。
分析
一个二维平面最短路问题。其实就是个有点麻(s)烦(b)的。方法有很多,可以跑两次BFS,找出Y和M都可以到达的KFC中总用时最短的。
我的解法类似,但不想写2个函数,就用一个标记表示是Y还是M的路径。使用一个队列完成两次BFS。
AC代码
//#include <bits/stdc++.h> #include <iostream> #include <iomanip> #include <bitset> #include <string> #include <set> #include <stack> #include <sstream> #include <list> //#include <array> #include <vector> #include <queue> #include <map> #include <memory> #include <iterator> #include <climits> #include <cassert> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) #define all(x) (x).begin(), (x).end() typedef long long ll; typedef unsigned int UINT; typedef unsigned long long ull; typedef pair<int, int> pdi; typedef pair<ll, int> pli; int const maxn = 200 + 10; const int INF = 0x3f3f3f3f; const ll INFL = 0x3f3f3f3f3f3f3f3f; inline int lc(int x) {return x << 1;} inline int rc(int x) {return x << 1 | 1;} int n, m; int vis1[maxn][maxn]; int vis2[maxn][maxn]; int mp2[maxn][maxn]; char mp[maxn][maxn]; int dis1[maxn][maxn], dis2[maxn][maxn]; struct node { int x, y, step, v; node(int x = 0, int y = 0, int step = 0, int v = 0) : x(x), y(y), step(step), v(v) { } }; int const dir[4][2] = {1, 0, 0, 1, -1, 0, 0, -1}; int x1, x2, y1, y2; void bfs() { queue<node> q; q.push(node(x1, y1, 0, 1)); q.push(node(x2, y2, 0, 2)); while (!q.empty()) { node tq = q.front(); q.pop(); for (int i = 0; i < 4; i++) { int tx = tq.x + dir[i][0]; int ty = tq.y + dir[i][1]; if (tx >= 0 && tx < n && ty >= 0 && ty < m && mp[tx][ty] != '#') { if (tq.v == 1) { if (vis1[tx][ty]) continue; q.push(node(tx, ty, tq.step + 1, 1)); vis1[tx][ty] = 1; dis1[tx][ty] = tq.step + 1; } else { if (vis2[tx][ty]) continue; q.push(node(tx, ty, tq.step + 1, 2)); vis2[tx][ty] = 1; dis2[tx][ty] = tq.step + 1; } } } } } int main(void) { FAST_IO; while (cin >> n >> m) { memset(dis1, 0x3f, sizeof(dis1)); memset(dis2, 0x3f, sizeof(dis2)); memset(vis1, 0, sizeof(vis1)); memset(vis2, 0, sizeof(vis2)); for (int i = 0; i < n; i++) { cin >> mp[i]; for (int j = 0; j < m; j++) { if (mp[i][j] == 'Y') { x1 = i, y1 = j; } if (mp[i][j] == 'M') { x2 = i, y2 = j; } } } vis1[x1][y1] = vis1[x2][y2] = 1; vis2[x1][y1] = vis2[x2][y2] = 1; dis1[x1][y1] = 0; dis2[x2][y2] = 0; bfs(); int ans = INF; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (mp[i][j] == '@' && dis1[i][j] != INF && dis2[i][j] != INF && dis1[i][j] + dis2[i][j] <= ans) { ans = dis1[i][j] + dis2[i][j]; } } } cout << ans * 11 << endl; } return 0; }
HDU - 1875 MST
题意
有N个岛屿,现在需要施工,要求让N个岛屿全都连通,并且2个小岛之间的距离不能小于10米,也不能大于1000米,并且花费最小。若是无法实现,则输出"oh!"。
分析
还是最小生成树的裸题,是需要判断岛的距离大于等于10小于等于1000就行了。
AC代码
//#include <bits/stdc++.h> #include <iostream> #include <iomanip> #include <bitset> #include <string> #include <set> #include <stack> #include <sstream> #include <list> //#include <array> #include <vector> #include <queue> #include <map> #include <memory> #include <iterator> #include <climits> #include <cassert> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) #define all(x) (x).begin(), (x).end() typedef long long ll; typedef unsigned int UINT; typedef unsigned long long ull; typedef pair<int, int> pdi; typedef pair<ll, int> pli; int const maxn = 100 * 100 + 10; const int INF = 0x3f3f3f3f; const ll INFL = 0x3f3f3f3f3f3f3f3f; inline int lc(int x) {return x << 1;} inline int rc(int x) {return x << 1 | 1;} struct point { int x, y; point(const int &x = 0, const int &y = 0) : x(x), y(y) { } }p[maxn]; double dis(point &x, point &y) { return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y)); } struct node { int u, v; double w; bool operator<(const node &x) const { return w < x.w; } }e[maxn]; int fa[maxn]; int cnt, setnum; bool check(const double &w1) { if (w1 < 10 || w1 > 1000) return false; return true; } int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); } bool unio(int x, int y) { int fx = find(x), fy = find(y); if (fx != fy) { fa[fx] = fy; return false; } return true; } void init(int n) { cnt = 0; setnum = n; for (int i = 0; i <= n; i++) { fa[i] = i; } } double kruskal() { sort(e, e + cnt); double ans = 0; for (int i = 0; i < cnt; i++) { if (!unio(e[i].u, e[i].v)) { setnum--; ans += e[i].w; } } return ans; } int main(void) { FAST_IO; int t; cin >> t; while(t--) { int n; cin >> n; init(n); for (int i = 0; i < n; i++) { cin >> p[i].x >> p[i].y; } for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { double w = dis(p[i], p[j]); if (check(w)) { e[cnt++] = {i, j, w}; } } } double ans = kruskal(); if (setnum != 1) cout << "oh!" << endl; else cout << fixed << setprecision(1) << ans * 100 << endl; } return 0; }
POJ - 2018 二分
题意
给定一个正整数数列,求一个平均数最大的,长度不小于L的子段。
分析
蓝书上经典二分答案判定。
若二分的值域为长度>=L子段的平均值,则可以把数组中的每个数字减去这个值,就转换成了"是否存在一个长度不小L的子段和>=0"的问题。
而对于这个问题的判断,可以在的时间复杂度内实现。使用前缀和的性质,子段和可以转换成前缀和减法。即:
随着i的增长,j的取值范围都没增大,所以只需要每次枚举j,记录最小的就行了。
总复杂度:check函数,二分,总共为
AC代码
//#include <bits/stdc++.h> #include <iostream> #include <iomanip> #include <bitset> #include <string> #include <set> #include <stack> #include <sstream> #include <list> // #include <array> #include <vector> #include <queue> #include <map> #include <memory> #include <iterator> #include <climits> #include <cassert> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) #define all(x) (x).begin(), (x).end() typedef long long ll; typedef unsigned int UINT; typedef unsigned long long ull; typedef pair<int, int> pdi; typedef pair<ll, int> pli; int const maxn = 1e5 + 10; inline int lc(int x) {return x << 1;} inline int rc(int x) {return x << 1 | 1;} double a[maxn], b[maxn], sum[maxn]; int main(void) { // FAST_IO; int n, L; // cin >> n >> L; scanf("%d %d", &n, &L); for (int i = 1; i <= n; i++) { // cin >> a[i]; scanf("%lf", &a[i]); } double eps = 1e-5; double l = -1e6, r = 1e6; while (r - l > eps) { double mid = (l + r) / 2; for (int i = 1; i <= n; i++) { b[i] = a[i] - mid; } for (int i = 1; i <= n; i++) { sum[i] = (sum[i - 1] + b[i]); } double ans = -1e10; double min_val = 1e10; for (int i = L; i <= n; i++) { min_val = min(min_val, sum[i - L]); ans = max(ans, sum[i] - min_val); } if (ans >= 0) l = mid; else r = mid; } // cout << (int)(r * 1000) << endl; printf("%d\n", (int)(r * 1000)); return 0; }
总结
今天寒假开始第一天,写了一些题。但大多还是都是水题、裸题。。。就当状态康复吧。
今天这些题中,比较的经典的:floyd求传递闭包、二分答案求子段和,这两题还是挺具有代表性,比较增长视野的。