Quantcast
Channel: AlanYume »次小生成树
Viewing all articles
Browse latest Browse all 2

HDU 4081 Qin Shi Huang’s National Road System (次小生成树变形)

$
0
0

题目链接:Qin Shi Huang's National Road System


题意

有n个城市,秦始皇要修用n-1条路把它们连起来,要求从任一点出发,都可以到达其它的任意点。秦始皇希望这所有n-1条路长度之和最短。然后徐福突然有冒出来,说是他有魔法,可以不用人力、财力就变出其中任意一条路出来。
秦始皇希望徐福能把要修的n-1条路中最长的那条变出来,但是徐福希望能把要求的人力数量最多的那条变出来。对于每条路所需要的人力,是指这条路连接的两个城市的人数之和。
最终,秦始皇给出了一个公式,A/B,A是指要徐福用魔法变出的那条路所需人力, B是指除了徐福变出来的那条之外的所有n-2条路径长度之和,选使得A/B值最大的那条。


分析:

要使得A/B最大,则B应尽量小,A尽量大。
而这n-2条边的最小值显然会在最小生成树的n-1条边中选出。
注意:这样的最小生成树可能会有多颗,但这并不妨碍我们接下来的计算。
因为我们会对每一条边都考虑一次加入操作,并为了防止在生成树上出现环,再断开在环上边权最大的边,以使得B尽可能小。而这里的A就是被考虑加入生成树上的边连接的两做城池的人口总和。
最终,这样枚举完所有边,并取所有A/B值中的最大值即是答案。

 
生成树1
加入一条边,为了任然满足树的要求,必须在树上断开一条边(这里要求B尽可能小,则应断开权值最大的边)。而人口就是加入的边的连接的两座城池人口之和。
次小生产树

 

 

下面附上两份代码:

代码1:(在Prim中用优先队列实现每次弹出一个minCost最小的点的编号,wrong answer)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;

const double kDNF = 1000000000;
const int kMaxN = 1010;

struct City{
	int x, y, popu;
};


int n;
City city[kMaxN];
double edge[kMaxN][kMaxN], max_edge[kMaxN][kMaxN];
int pre[kMaxN];
bool used[kMaxN];
double total_cost, min_cost[kMaxN];

struct Node{
	int id;

	Node() {}
	Node(int id_) : id(id_) {}

	bool operator < (const Node &n) const{
		return min_cost[id] > min_cost[n.id];
	}
};

void Read(){
	scanf("%d", &n);
	for(int i = 0; i < n; i++) scanf("%d %d %d", &city[i].x, &city[i].y, &city[i].popu);
}

inline double GetDist(const City &c1, const City &c2){
	return sqrt(1.0 *((c1.x - c2.x) * (c1.x - c2.x) + (c1.y - c2.y) * (c1.y - c2.y)));
}

void MakeEdge(){
	for(int i = 0; i < n; i++){
		for(int j = 0; j < i; j++){
			edge[i][j] = edge[j][i] = GetDist(city[i], city[j]);
		}
		edge[i][i] = kDNF;
	}
}

void Prim(){
	for(int i = 0; i < n; i++){
		used[i] = false;
		min_cost[i] = kDNF;
		for(int j = 0; j < n; j++){
			max_edge[i][j] = 0;
		}
	}
	total_cost = 0;
	priority_queue<Node> que;
	min_cost[0] = 0;
	pre[0] = -1;
	que.push(Node(0));
	while(!que.empty()){
		Node now_n = que.top(); que.pop();
		if(!used[now_n.id]){
			for(int i = 0; i < n; i++){
				if(used[i]){
					max_edge[now_n.id][i] = max_edge[i][now_n.id] = max(max_edge[i][pre[now_n.id]], min_cost[now_n.id]);
				}else{
					if(min_cost[i] > edge[i][now_n.id]){
						min_cost[i] = edge[i][now_n.id];
						pre[i] = now_n.id;
						que.push(Node(i));
					}
				}
			}
			used[now_n.id] = true;
			total_cost += min_cost[now_n.id];
		}
	}
}

void GetAns(){
	double ret = 0;
	for(int i = 0; i < n; i++){
		for(int j = 0; j < i; j++){
			ret = max(ret, 1.0*(city[i].popu + city[j].popu) / (total_cost - max_edge[i][j]));
		}
	}
	printf("%.2f\n", ret);
}

void Solve(){
	MakeEdge();
	Prim();
	GetAns();
}

int main(){
	int test;
	scanf("%d", &test);
	while(test--){
		Read();
		Solve();
	}
	return 0;
}

我的猜测是,在优先队列里的比较函数里的比较值不是Node的成员变量,并且在某个节点编号已经入队的前提下,后续步骤中仍在更新minCost的值,然而入队了的元素的比较函数的minCost不再更新 导致优先队列里的元素排序出错,然后一直wrong的。

 

 

代码2:(O(n^2)普通Prim)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <cmath>
using namespace std;

const double kDNF = 1000000000;
const int kMaxN = 1010;

struct City{
	int x, y, popu;
};


int n;
City city[kMaxN];
double edge[kMaxN][kMaxN], max_edge[kMaxN][kMaxN];
int pre[kMaxN];
bool used[kMaxN];
double total_cost, min_cost[kMaxN];

void Read(){
	scanf("%d", &n);
	for(int i = 0; i < n; i++) scanf("%d %d %d", &city[i].x, &city[i].y, &city[i].popu);
}

inline double GetDist(const City &c1, const City &c2){
	return sqrt(1.0 *((c1.x - c2.x) * (c1.x - c2.x) + (c1.y - c2.y) * (c1.y - c2.y)));
}

void MakeEdge(){
	for(int i = 0; i < n; i++){
		for(int j = 0; j < i; j++){
			edge[i][j] = edge[j][i] = GetDist(city[i], city[j]);
		}
		edge[i][i] = kDNF;
	}
}

void Prim(){
	for(int i = 0; i < n; i++){
		used[i] = false;
		min_cost[i] = kDNF;
		for(int j = 0; j < n; j++){
			max_edge[i][j] = 0;
		}
	}
	total_cost = 0;
	min_cost[0] = 0;
	pre[0] = -1;
	for(int k = 0; k < n; k++){
		int u = -1;
		for(int v = 0; v < n; v++){
			if(!used[v] && (u == -1 || min_cost[u] > min_cost[v])) u = v;
		}
			for(int i = 0; i < n; i++){
				if(used[i]){
					max_edge[u][i] = max_edge[i][u] = max(max_edge[i][pre[u]], min_cost[u]);
				}else{
					if(min_cost[i] > edge[i][u]){
						min_cost[i] = edge[i][u];
						pre[i] = u;
					}
				}
			}
			used[u] = true;
			total_cost += min_cost[u];
	}
}

void GetAns(){
	double ret = -1;
	for(int i = 0; i < n; i++){
		for(int j = 0; j < i; j++){
			ret = max(ret, 1.0*(city[i].popu + city[j].popu) / (total_cost - max_edge[i][j]));
		}
	}
	printf("%.2f\n", ret);
}

void Solve(){
	MakeEdge();
	Prim();
	GetAns();
}

int main(){
	int test;
	scanf("%d", &test);
	while(test--){
		Read();
		Solve();
	}
	return 0;
}

 


Viewing all articles
Browse latest Browse all 2

Latest Images

Trending Articles





Latest Images