您的位置:首页 > 其它

HDU 4081 Qin Shi Huang's National Road System prim + (DP 或 树形DP) 好题

2012-10-07 22:30 344 查看
题意:

有n个(n<=1000)城市,告诉坐标(int),边的权值就是两点的距离,并且每个城市都有人居住,现在要修路n-1条路,使得每个城市都连通。现能让一条边可以不用任何花费。求 这条边的两端点的总人数/(包含这条边的最小生成树的总权值-这条边的权值)最大值。即(Wa+Wb)/(mst-w(a,b))最大。

思路:

先求该图的最小生成树,prim,O(n^2);

方法一: 枚举边

枚举最小生成树的每条边,去掉这条边,最小生成树变成了2个各自连通的树,假设为树A,B。分别找到树A,B中人口最多的两个点,这两个点连起来就是去掉这条边所取得的最大比例。用树形DP可以求这个最大比例。

方法二: 枚举点

在求最小生成树的过程中,预处理求出点i到点j之间在最小生成树里的最大边,用dp[i][j] 保存。枚举每两个点,求出最大值。

方法一:

处理1:

1. 用dp[i][j]表示树A中的点i 到 树B及      其子树(j点所在的树)的最大人口,这个过程可以在一边dfs就可以出来,对于每个 i 的dfs 复杂度是O(n) ,外加一个n的循环求出每个点,这里的总复杂度为 O(n^2)。

2. 通过求出来的dp[i][j] 再用一个dfs 求出 树B 到 树A的最大人口,(方法:枚举树A中的所有点 到 树B的最大人口,取其中的最小值。)显然, 这个求出来的值是我们要的去掉这条边所取得的最大比例。

代码1:

View Code

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
using namespace std;
#define maxn 1003
#define inf 1000000000

int x[maxn], y[maxn], p[maxn];
double map[maxn][maxn];
double dis[maxn];
int pre[maxn];
bool vis[maxn];
double mst;
double dp[maxn][maxn];

double maxz(double a, double b)
{
return a > b ? a : b;
}

int n, m;
double ans;

double Dis(int x1, int y1, int x2, int y2)
{
return sqrt((x1 - x2) * (x1 - x2) * 1.0 + (y1 - y2) * (y1 - y2) * 1.0);
}

void prim()
{
int i, j, k;
memset(dp, 0, sizeof(dp));
for(i = 1; i < n; i++)
dis[i] = map[0][i], vis[i] = 0, pre[i] = 0;
dis[0] = inf, vis[0] = 1, pre[0] = -1;

mst = 0;
for(i = 0; i < n-1; i++)
{
k = 0;
for(j = 1; j < n; j++)
if(!vis[j] && dis[k] > dis[j])
k = j;
vis[k] = 1;
mst += dis[k];
dp[pre[k]][k] = dp[k][pre[k]] = map[k][pre[k]];

for(j = 1; j < n; j++)
if(!vis[j] && dis[j] > map[k][j])
dis[j] = map[k][j], pre[j] = k;
for(j = 1; j < n; j++)
if(vis[j] && j != k)
dp[j][k] = dp[k][j] = maxz(dp[j][pre[k]], dp[pre[k]][k]);
}
}

void solve()
{
int i, j;
double ans = 0;
for(i = 0; i < n ;i++)
for(j = 0; j < n; j++)
{
if(i == j) continue;
double tmp = (p[i] + p[j]) * 1.0/ (mst - dp[i][j]);
if(tmp > ans) ans = tmp;
}
printf("%.2f\n", ans);
}

int main()
{
int i, j, cas;
scanf("%d", &cas);
while(cas--)
{
scanf("%d", &n);
for(i = 0; i < n; i++)
scanf("%d%d%d", &x[i], &y[i], &p[i]);
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
if(i != j)
map[i][j] = map[j][i] = Dis(x[i], y[i], x[j], y[j]);
prim();
solve();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: