D - Qin Shi Huang’s National Road System
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <stack>
using namespace std;
const double eps = 1e-6;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e3 + 20;
struct city {
int x, y, p;
} c[MAXN];
int V, par[MAXN]; // par数组记录生成树上i的上一个点
bool used[MAXN][MAXN], visit[MAXN];
double mincost[MAXN], cost[MAXN][MAXN], dp[MAXN][MAXN];
double prim() {
memset(visit, false, sizeof(visit));
memset(dp, 0, sizeof(dp));
memset(used, false, sizeof(used));
for (int i = 1; i <= V; i++) {
mincost[i] = cost[1][i];
par[i] = 1;
}
visit[1] = true;
double res = 0;
for (int i = 1; i < V; i++) {
int v = -1;
for (int j = 1; j <= V; j++)
if (!visit[j] && (v == -1 || mincost[j] < mincost[v]))
v = j;
if (v == -1)
break;
visit[v] = true;
used[v][par[v]] = used[par[v]][v] = true;
res += mincost[v];
for (int j = 1; j <= V; j++) {
if (!visit[j] && cost[v][j] < mincost[j]) {
mincost[j] = cost[v][j];
par[j] = v;
}
if (visit[j] && j != v) {
dp[v][j] = dp[j][v] = max(dp[j][par[v]], mincost[v]);
}
}
}
return res;
}
int main() {
int tcase;
scanf("%d", &tcase);
while (tcase--) {
scanf("%d", &V);
for (int i = 1; i <= V; i++)
scanf("%d%d%d", &c[i].x, &c[i].y, &c[i].p);
for (int i = 1; i <= V; i++)
for (int j = i + 1; j <= V; j++)
cost[i][j] = cost[j][i] =
sqrt((c[i].x - c[j].x) * (c[i].x - c[j].x) +
(c[i].y - c[j].y) * (c[i].y - c[j].y));
double mst = prim();
double ans = -1;
for (int i = 1; i <= V; i++) {
for (int j = i + 1; j <= V; j++) {
if (used[i][j]) // i和j之间的边是否在最小生成树上
ans =
max(ans, (c[i].p + c[j].p + 0.0) / (mst - cost[i][j]));
else
ans = max(ans, (c[i].p + c[j].p + 0.0) / (mst - dp[i][j]));
}
}
printf("%.2lf\n", ans);
}
return 0;
}
题解:我们可以先建一棵最小生成树,然后再从这n个城市中选择2个城市,把二者之间的路看做用法术造的路。如果这条路在最小生成树上,即去掉这条边后最小生成树会变成两棵独立的树,那么A/B就是(二者城市的人口数总和/最小生成树的值减去二者之间的长度);如果这条路不在生成树上,那么为了使A/B最大,我们要在最小生成树上删除一条权值最大的边使得生成树分为两棵树,同时这2个城市不在同一颗树上,然后就可以把这两个城市之间的路视为用法术造出的路,A/B就是
(二者城市的人口数总和/最小生成树的值减去二者之间某条使二者连通的边的最大长度)。
综上:我们可以使用used[i][j]表示i与j之间的边是否在最小生成树上,dp[i][j]表示i到j之间的使得i与j连通的权值最大的边的权值。在prim内更新dp数组。题解链接
E - Stream My Contest
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
typedef long long ll;
const int maxn = 1e4 + 10;
const ll INF = 1e15 + 10;
using namespace std;
struct P {
int u, v, b;
ll c;
P() {}
P(int u, int v, int b, ll c) : u(u), v(v), b(b), c(c) {}
} p[maxn];
int L, R, nn, m, T, t = 1, limit;
int from, to, kb, cost;
ll d[100];
int id[100], par[100], vis[100];
vector<P> g;
bool operator<(P a, P b) {
return a.c < b.c;
}
bool Dmst(int val, int root, int n) {
ll ans = 0;
for (int i = 0; i < m; i++)
p[i] = g[i];
while (1) {
fill(d, d + 100, INF);
d[root] = 0;
memset(vis, -1, sizeof(vis));
memset(id, -1, sizeof(id));
for (int i = 0; i < m; i++) {
if (p[i].b < val || p[i].u == p[i].v)
continue;
if (p[i].v == root || p[i].c >= d[p[i].v])
continue;
d[p[i].v] = p[i].c;
par[p[i].v] = p[i].u;
}
for (int i = 0; i < n; i++) {
if (i == root)
continue;
if (d[i] == INF)
return false;
}
int cnt = 0;
for (int i = 0; i < n; i++) {
ans += d[i];
int v = i;
while (vis[v] != i && id[v] < 0 && v != root) {
vis[v] = i;
v = par[v];
}
if (v == root || id[v] >= 0)
continue;
for (int u = par[v]; u != v; u = par[u])
id[u] = cnt;
id[v] = cnt++;
}
if (!cnt)
break;
for (int i = 0; i < n; i++)
if (id[i] < 0)
id[i] = cnt++;
for (int i = 0; i < m; i++) {
int v = p[i].v;
p[i].u = id[p[i].u];
p[i].v = id[p[i].v];
if (p[i].u != p[i].v)
p[i].c -= d[v];
}
n = cnt;
root = id[root];
}
return ans <= limit;
}
int low(int l, int r) {
while (l < r) {
int mid = l + ((r - l + 1) >> 1);
bool res = Dmst(mid, 0, nn);
if (!res)
r = mid - 1;
else
l = mid;
}
return l;
}
int main() {
scanf("%d", &T);
while (T--) {
g.clear();
scanf("%d %d %d", &nn, &m, &limit);
L = 1e9;
R = 0;
for (int i = 0; i < m; i++) {
scanf("%d %d %d %d", &from, &to, &kb, &cost);
g.push_back(P(from, to, kb, (ll)cost));
R = max(R, kb);
L = min(L, kb);
}
sort(g.begin(), g.end());
bool can = Dmst(L, 0, nn);
printf("Case %d: ", t++);
if (!can) {
printf("impossible\n");
continue;
}
int ans = low(L, R);
printf("%d kbps\n", ans);
}
return 0;
}
题解:这里是有向图的最小生成树+二分答案,二分那个网络的宽带,然后跑朱刘算法,朱刘算法就是找出有向图的最小生成树的权值和。题解链接----算法链接