因为不够方便, 因此将 \(T1\) 到 \(T4\) 的题面放到其余里面了, 请谅解!
还有模拟赛的连接 传送门html
咱们能够这么想, 既然要求 \(A_m + A_n + A_p = A_i\), 那么根据 \(n^3\) 的算法来算 \(5000\) 的 \(n\) 是绝对会超时的, 因此咱们考虑用小学的知识 : 移项
可得 \(A_m + A_n = A_i - A_p\)
这样就能够二维处理 \(A_m + A_n\) 再存进 \(hash\) 表里面, 再用 \(A_i - A_p\) 去匹配。 若是匹配成功了, 那么就存在 \(A_m + A_n + A_p = A_i\), \(ans\) 就 \(++\) 了c++
#include <bits/stdc++.h> #define N 5005 #define M 25000004 #define hu 689207157 #pragma GCC optimize(2) using namespace std; int n, ans, flag; int a[N], hash[M]; int hhd (int x) { int te = (x % M + M) % M; int i = 0; while (i < M && hash[(te + i) % M] != x && hash[(te + i) % M] != hu) i ++; return (te + i) % M; } void insert_hhd (int x) { int dw = hhd (x); hash[dw] = x; } int main () { freopen ("good.in", "r", stdin); freopen ("good.out", "w", stdout); scanf ("%d", &n); for (int i = 0; i < M; ++ i) hash[i] = hu; for (int i = 1; i <= n; ++ i) { flag = 0; scanf ("%d", &a[i]); for (int j = 1; j < i; ++ j) { if (hash[hhd(a[i] - a[j])] == a[i] - a[j] && !flag) { ans ++; flag = 1; break; } } for (int j = 1; j <= i; ++ j) insert_hhd (a[i] + a[j]); } printf ("%d", ans); return 0; }
能够理解为一排排好序的数组, 每一个点都有x, y坐标。
因此咱们能够用经典的欧氏距离来求出两个点之间的距离。算法
而后注意题目要求走彻底部点数组
每一个点通过一次闭包
而后能够把来去理解为从 \(A\) 点出发, 走两个不相交的路径到达点 \(B\), 因而这道题又能够转化为
两条不相交路径最短问题
而后就是 \(DP\), 设状态 \(f[i][j]\) 为从点 \(A\) (编号\(1\)) 的两条路径分别走到 编号为 \(i\) 和 编号为 \(j\) 的两个点, 再注意判断一些特殊的点便可。
注意, 要杜绝 \(i == j\) 的状况 (当 \(i == j == n\)的状况除外, 由于这样是走到终点了)。
还有要继续往前就用一个 \(Max(i, j) + 1\) 便可, 即当前所走的最大编号的点再往前走一步spa
#include <bits/stdc++.h> #define db double #define N 1005 using namespace std; int n, b1, b2; db hhd[N][3], f[N][N]; db dis (int hu, int he) // 传统的欧氏距离 { db x = (db)(hhd[hu][1] - hhd[he][1]) * (hhd[hu][1] - hhd[he][1]); db y = (db)(hhd[hu][2] - hhd[he][2]) * (hhd[hu][2] - hhd[he][2]); return (db)sqrt (x + y); } void run () { int i, j; for (int i = 1; i <= n; ++ i) for (int j = 1; j <= n; ++ j) f[i][j] = 123456789; f[1][1] = 0; for (int i = 1; i <= n; ++ i) for (int j = 1; j <= n; ++ j) { if (i != j || i == 1) { int k = max (i, j) + 1; if (k == n + 1) { if (j == n) f[n][n] = min (f[n][n], f[i][n] + dis (i, n)); if (i == n) f[n][n] = min (f[n][n], f[n][j] + dis (j, n)); } else { if (k != b1) f[i][k] = min (f[i][k], f[i][j] + dis(j, k)); //回的时候不走b1 if (k != b2) f[k][j] = min (f[k][j], f[i][j] + dis(i, k)); } } } printf ("%.2lf", f[n][n]); } int main () { freopen ("path.in", "r", stdin); freopen ("path.out", "w", stdout); scanf ("%d%d%d", &n, &b1, &b2); b1 += 1, b2 += 1; // 编号是从0开始的 for (int i = 1; i <= n; ++ i) scanf ("%lf%lf", &hhd[i][1], &hhd[i][2]); run (); return 0; }
虽然没有调出来, 可是思路我仍是很清晰的~.net
有一个前提的基础, 以下图
而后就不会了
看看机房dalao的博客传送门code
第一个思路, 暴力;
能够跑 \(Dijstkra\) 来肯定哪些点是否相通, 而后再暴力枚举, 判断最多能有多少个不相通且两两间不相邻的点。htm
正解 : 传递闭包。
其实就是一个二分图的模板加上 \(floyd\) 就能过了(数据水?)blog
#include <bits/stdc++.h> #define N 205 using namespace std; int n, m, ans; int G[N][N], vis[N], link[N]; int found (int x) { for (int i = 1; i <= n; ++ i) if (!vis[i] && G[x][i]) { vis[i] = 1; if (!link[i] || found (link[i])) { link[i] = x; return 1; } } return 0; } int main () { scanf ("%d%d", &n, &m); for (int i =1; i <= m; ++ i) { int x, y; scanf ("%d%d", &x, &y); G[x][y] = 1; } for (int k = 1;k <= n; ++ k) for (int i = 1; i <= n; ++ i) for (int j = 1; j <= n; ++ j) if (G[i][k] && G[k][j]) G[i][j] = 1; for (int i = 1; i <= n; ++ i) { memset (vis, 0, sizeof (vis)); ans += found (i); } printf ("%d", n - ans); return 0; }