次短路 优先队列优化 -- Qin Shi Huang's National Road System HDU - 4081

Qin Shi Huang’s National Road System HDU - 4081

题意:
出N个点的坐标以及每个点的人口, 要求将这N个点通过N-1条边连接起来, 权值为两点直接距离, B为距离和, 同时可以选中一条边, 使得该边权值变为0, A为该边两点人口数量. 求A/B的最大值

思路:
题目要求A / B最大,A是相邻两座城市的总人口数,B是除该相邻城市之间权值的最短路,遍历所有两两座城市相邻的情况(A / B)取max即可,A很好得到,两个for循环就可遍历所有两两相邻的城市总人数情况,主要是B的值,B的值可以用次短路的思想求解。

dalao的code:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef  long long LL;
const int inf = 1 << 30;
const LL maxn = 1010;

int N;
double w[maxn][maxn];
struct node {
    int x, y;
    int p;
    node(int xx, int yy, int pp) {x = xx, y = yy, p = pp;}
    node() {}
} vs[maxn];
double getDis(int x1, int y1, int x2, int y2) {
    return sqrt((double)(x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));
}

double d[maxn];
bool used[maxn];
double maxD[maxn][maxn];   //MST中从i->j的最大权值
int pre[maxn];          //某一点父节点
bool mst[maxn][maxn];   //该点是否已经在MST中
typedef pair<int, int> P;
double Prim(int s) {
    fill(d, d + maxn, inf);
    fill(pre, pre+maxn, s);
    ms(maxD, 0); ms(used, 0); ms(mst, 0);
    priority_queue<P, vector<P>, greater<P> > q;
    q.push(P(d[s] = 0, s));
    double res = 0;
    while(!q.empty()) {
        P cur = q.top();
        q.pop();
        int u = cur.second;
        if(used[u])
            continue;
        used[u] = true, res += d[u];
        mst[u][pre[u]] = mst[pre[u]][u] = true; //加入到MST中
        for(int v = 1; v <= N; ++v) {
            if(used[v] && w[u][v] < inf)        //只更新MST中的
                maxD[u][v] = maxD[v][u] = max(maxD[pre[u]][v], d[u]);
            if(w[u][v] < d[v]) {
                d[v] = w[u][v];
                pre[v] = u;                     //更新父节点
                q.push(P(d[v], v));
            }
        }
    }
    return res;
}
int main() {
    int T, a, b, c;
    scanf("%d",&T);
    while(T--) {
        ms(vs, 0); fill(w[0], w[0]+maxn*maxn, inf);
        scanf("%d",&N);
        for(int i = 1; i <= N; ++i) {
            scanf("%d%d%d",&a,&b,&c);
            vs[i] = node(a, b, c);
        }
        for(int i = 1; i < N; ++i)
            for(int j = i+1; j <= N; ++j)
                w[i][j] = w[j][i] = getDis(vs[i].x, vs[i].y, vs[j].x, vs[j].y);

        //枚举删边, 找出最大值
        double B = Prim(1), A, ans = -1;
        for(int i = 1; i < N; ++i)
            for(int j = i+1; j <= N; ++j){
                A = vs[i].p+vs[j].p;
                //这条边未在MST中使用, 尝试加边并删去生成环中的最长边, 已使用则直接变0
                if(mst[i][j]){
                    ans = max(ans, A/(B-w[i][j]));
                }else{
                    ans = max(ans, A/(B-maxD[i][j]));
                }
            }
        printf("%.2lf\n", ans);
    }

    return 0;
}

我的sb code:
bug一下找不出,暂时懒得找了



#include<algorithm>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
using namespace std;

const int maxn = 1e3 + 5;

double mp[maxn][maxn], maxd[maxn][maxn], d[maxn];
int  pre[maxn];
bool vis[maxn], opt[maxn][maxn];
struct point_city{
	 double x, y, w;
}city[maxn];

double prim(int n){
   memset(opt, 0, sizeof(opt));
   memset(vis, 0, sizeof(vis));
   memset(maxd, 0, sizeof(maxd));
   memset(opt, 0, sizeof(opt));
   for(int i = 1; i <= n; i++)
   d[i] = mp[1][i], pre[i] = 1;
   vis[1] = 1;
   
   int p;
   double ans = 0;
   for(int i = 1; i < n; i++){
   	  p = -1;
   	  for(int j = 1; j <= n; j++)
   	  if(!vis[j] && (p == -1 || d[p] >d[j])) p = j;
   	  
   	  vis[p] = 1;
   	  if(p == -1) break;
   	  ans += d[p];
   	  
   	  for(int j = 1; j<= n; j++){
   	  	if(vis[j]) {
   	  		maxd[j][p] = maxd[p][j] = max(maxd[j][pre[p]],mp[pre[p]][p]);
			opt[j][p] = opt[p][j] = 1;
			opt[p][pre[p]] = opt[pre[p]][p] = 0;
	    }
   	  	if(d[j] > mp[p][j]) d[j] = mp[p][j], pre[j] = p;    
	  } 
   	
   }
   return ans;
	
}

int main()
{
	int t, n, x, y, w;
	int cot;
	scanf("%d", &t);
	while(t--){
		cot = 0;
		scanf("%d", &n);
		for(int i = 1; i <= n; i++)
			scanf("%lf%lf%lf", &city[i].x, &city[i].y, &city[i].w);
			
		memset(mp, 0x3f, sizeof(mp));		
		for(int i = 1; i <= n; i++)
		for(int j = i + 1; j <= n; j++){
			double res = sqrt((city[i].x - city[j].x) * (city[i].x - city[j].x) +
			               (city[i].y - city[j].y) * (city[i].y - city[j].y));
			mp[i][j] = mp[j][i] = res;
			
		}
		
	    double res = prim(n);
		double sum = 0;
		for(int i = 1; i <= n; i++)
		for(int j = i + 1; j <= n; j++){
			if(opt[i][j])  sum = max(sum, (city[i].w + city[j].w) / (res - maxd[i][j]));
			else sum = max(sum, (city[i].w + city[j].w) / (res - mp[i][j]));
		
        }
       
	     printf("%.2lf\n", sum);
	     
      }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值