您的位置:首页 > 其它

枚举+最小生成树 hdoj4081 Qin Shi Huang's National Road System

2017-05-21 18:26 489 查看
题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4081

题目大意是给你一个图,既有点权又有边权,求一个生成树,你可以任选其中一条边,使它的边权为0,最终使得边权为0的两点的点权和/剩余边的边权和 最小。

我们可以这样思考,从最小生成树中去掉一条边,剩余的两个连通分量用边权为0的边链接。这样是正确的,因为如果你要选择两个点并用0边把它们连接起来,意味着需要在此基础上做一颗最小生成树,求最小生成树的算法是贪心的,因此在此基础上的最小生成树除了0边之外,其他边的集合是原来最小生成树的子集(或者如果存在多棵最小生成树,边权和是相同的)。接下来就是选择0边连接哪两个节点的问题了,显然连接两个点权最大的节点。枚举删去n-1条边,取最大的即是答案。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
const int MAX = 1005;
const double INF = 1e10;
double map[MAX][MAX], xx[MAX], yy[MAX];
int n, p[MAX], vis[MAX], fu, fv, pmax;
struct edge {
int u, v;
double w;
} dis[MAX], e[MAX];
vector<int> ve[MAX];
void init(void)
{
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; i ++)
ve[i].clear();
pmax = 0;
}
inline double dist(double x1, double y1, double x2, double y2)
{
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
int dfs(int x)
{
vis[x] = 1;
if (p[x] > pmax) pmax = p[x];
for (int i = 0; i < ve[x].size(); i ++) {
if (!vis[ve[x][i]] && !(fu == x && fv == ve[x][i] || fu == ve[x][i] && fv == x)) {
dfs(ve[x][i]);
}
}
}
double prim(void)
{
double ret = 0;
vis[1] = 1;
for (int i = 1; i <= n; i ++) {
dis[i].w = map[1][i];
dis[i].u = 1;
dis[i].v = i;
}
for (int i = 1; i <= n - 1; i ++) {
double min = INF;
int minid;
for (int j = 1; j <= n; j ++) {
if (!vis[j] && dis[j].w < min) {
min = dis[j].w;
minid = j;
}
}
vis[minid] = 1;
ret += min;
e[i] = dis[minid];
ve[e[i].u].push_back(e[i].v);
ve[e[i].v].push_back(e[i].u);
for (int j = 1; j <= n; j ++) {
if (!vis[j] && dis[j].w > map[minid][j]) {
dis[j].w = map[minid][j];
dis[j].u = minid;
}
}
}
return ret;
}
int main()
{
int t;
scanf("%d",&t);
while (t --) {
init();
scanf("%d",&n);
for (int i = 1; i <= n; i ++) {
scanf("%lf%lf%d",&xx[i], &yy[i], &p[i]);
for (int j = 1; j < i; j ++) {
map[i][j] = map[j][i] = dist(xx[i], yy[i], xx[j], yy[j]);
}
map[i][i] = INF;
}
double len = prim();
double ans = -INF;
int pp;
for (int i = 1; i <= n - 1; i ++) {
memset(vis, 0, sizeof(vis));
pmax = 0;
fu = e[i].u;
fv = e[i].v;
dfs(fu);
pp = 0;
pp += pmax;
memset(vis, 0, sizeof(vis));
pmax = 0;
dfs(fv);
pp += pmax;
ans = max(ans, pp / (len - e[i].w));
}
printf("%.2lf\n", ans);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dfs 最小生成树