@Metralix
2018-03-08T14:41:45.000000Z
字数 4349
阅读 992
最短路
题目大意
有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_JUDGEfreopen("in.txt","r",stdin);#endif // ONLINE_JUDGEint 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]));elseans=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;}