@Metralix
2018-03-08T22:41:45.000000Z
字数 4349
阅读 834
最短路
题目大意
有n个城市,要修一些路使得任意两个城市都能连通。但是有人答应可以不计成本的帮你修一条路,为了使成本最低,你要慎重选择修哪一条路。假设其余的道路长度为B,那条别人帮忙修的道路两端城市中的总人口为B,要找一个使A/B最大的方案。
解题思路
先求最小生成树,处理出MST中任意两点之间的最长边。因为别人只答应给修一条路,所以枚举这条路,用这条路替换下一条MST中最长的边(在加入这条路后构成的环中),比较求得A/B的最大值。实际上是把求次小生成树的一些后续操作改改。
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma commment(linker,"/STACK: 102400000 102400000")
using namespace std;
const double eps=1e-6;
const int INF=0x3f3f3f3f;
const int MAXN=1e3+20;
struct city
{
int x,y,p;
};
city 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()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif // ONLINE_JUDGE
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=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=1; j<=V; j++)
{
if(i==j)
continue;
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个点m条边的无向图, 每条边有一个危险度,有q个询问, 每次给出两个点s、t,找一条路, 使得路径上的最大危险度最小。
解题思路
首先,我们可以发现,如果求一个最小生成树, 那么任意两点, 在生成树上有唯一路径, 而且这条路径上的最大危险值一定最小。 但是n和q都太大, 如果直接顺着树走,每次询问最大复杂度O(n), 那么复杂度高达O(n^2),会超时。 我们知道, 并查集在用了路径压缩之后效率高达O(n), 但是却破坏了树形结构, 所以不能用路径压缩。 然而我们经常忽视了按秩合并这个方法, 即使不用路径压缩, 仅仅靠按秩合并, 复杂度也可低至O(logn)。 因此我们只需按秩合并, 然后询问的时候向根回溯就行了, 复杂度mlogn。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int maxn=55000;
const int maxm=250000;
const int INF=0x3f3f3f3f;
typedef long long LL;
struct HeapNode{
int d,u;
HeapNode(){}
HeapNode(int a,int b):d(a),u(b){}
bool operator<(const HeapNode& rhs) const{
return d>rhs.d;
}
};
struct EdgeNode{
int to;
int w;
int next;
};
struct Prim{
EdgeNode edges[maxm];
int head[maxn];
int edge,n;
void init(int n){
this->n=n;
memset(head,-1,sizeof(head));
edge=0;
}
void addedge(int u,int v,int c){
edges[edge].w=c,edges[edge].to=v,edges[edge].next=head[u],head[u]=edge++;
}
bool done[maxn];
int dis[maxn];
int pre[maxn];
int dep[maxn];
void prim(int s){
priority_queue<HeapNode>que;
for (int i=0;i<=n;i++) dis[i]=INF;
dis[s]=0;
memset(done,0,sizeof(done));
memset(dep,0,sizeof(dep));
que.push(HeapNode(0,s));
while (!que.empty()){
HeapNode x=que.top();
que.pop();
int u=x.u;
if (done[u]) continue;
done[u]=true;
for (int i=head[u];i!=-1;i=edges[i].next){
int v=edges[i].to;
int w=edges[i].w;
if (!done[v]&&dis[v]>w){
dis[v]=w;
pre[v]=u;
dep[v]=dep[u]+1;
que.push(HeapNode(dis[v],v));
}
}
}
}
}solver;
int n,m;
int fa[maxn],cost[maxn],L[maxn];
int anc[maxn][20];
int maxCost[maxn][20];
void preprocess(){
for (int i=1;i<=n;i++){
anc[i][0]=fa[i];
maxCost[i][0]=cost[i];
for (int j=1;(1<<j)<n;j++) anc[i][j]=-1;
}
for (int j=1;(1<<j)<n;j++){
for (int i=1;i<=n;i++){
if (anc[i][j-1]!=-1){
int a=anc[i][j-1];
anc[i][j]=anc[a][j-1];
maxCost[i][j]=max(maxCost[i][j-1],maxCost[a][j-1]);
}
}
}
}
int query(int p,int q){
int log;
if (L[p]<L[q]) swap(p,q);
for (log=1;(1<<log)<=L[p];log++);log--;
int ans=-INF;
for (int i=log;i>=0;i--){
if (L[p]-(1<<i)>=L[q]){
ans=max(ans,maxCost[p][i]);
p=anc[p][i];
}
}
if (p==q) return ans;
for (int i=log;i>=0;i--){
if (anc[p][i]!=-1&&anc[p][i]!=anc[q][i]){
ans=max(ans,maxCost[p][i]);
p=anc[p][i];
ans=max(ans,maxCost[q][i]);
q=anc[q][i];
}
}
ans=max(ans,cost[p]);
ans=max(ans,cost[q]);
return ans;
}
int main()
{
int cas=0;
while (~scanf("%d%d",&n,&m)){
if (cas!=0) puts("");
cas++;
solver.init(n);
for (int i=0;i<m;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
solver.addedge(x,y,z);
solver.addedge(y,x,z);
}
solver.prim(1);
for (int i=1;i<=n;i++){
fa[i]=solver.pre[i];
cost[i]=solver.dis[i];
L[i]=solver.dep[i];
}
preprocess();
int Q;
scanf("%d",&Q);
while (Q--){
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",query(x,y));
}
}
return 0;
}