uva10457题意:m条路,每条路上必须维持速度v,现在有一辆车,启动能量和结束能量为a, b,途中消耗能量为经过路径最大速度减去最小速度,现在每次循环给定起点终点,问最小能量花费
当时一开始想写瓶颈树的时候,跑完最小生成树,想得到两个点间最长的边的时候,考虑了很多复杂的方法,用dfs得到路线然后再跑一边,代码又臭又长。
这道题是个人赛的时候学长找的一道题,注意到q的值很小,所以对于每次询问都要进行计算,从小到大枚举边,作为最小值的边,然后再用比他大的边来跑kruskal,当起点和终点联通的时候(既处于同一个集合中),此条边减去最小值的边的值,便是ans。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int maxn=200+10;
const int maxm=1000+20;
const int INF=2147483647;
int G[maxn][maxn];
int n,m,q,be,ge,ANS;
struct Edge{
int u,v,w;
bool operator <(const Edge &rhs)const {
return w<rhs.w;
}
};
vector<Edge>edges;
int p[maxn];
int find(int x){
return p[x]==x?x:p[x]=find(p[x]);
}
int solve(int s,int t){
int ans=INF;
for(int M=0;M<edges.size();M++){
for(int i=0;i<n;i++)p[i]=i;
for(int i=M;i<edges.size();i++){
int a=find(edges[i].u),b=find(edges[i].v);
if(a!=b)
p[a]=b;
if(find(s)==find(t)){
ans=min(ans,edges[i].w-edges[M].w);
break;
}
}
}
return ans;
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
edges.clear();
for(int i=0;i<m;i++){
int u,v,w;
cin>>u>>v>>w;
edges.push_back((Edge){u-1,v-1,w});
}
sort(edges.begin(),edges.end());
cin>>be>>ge;
cin>>q;
while(q--){
int s,t;
cin>>s>>t;
int an=be+ge+solve(s-1,t-1);
cout<<an<<endl;
}
}
return 0;
}
在网上查资料说这个题是最小瓶颈树,然而看了一下定义,所谓的最小瓶颈树就是最大的一条边最小。最大最小最容易想到的是二分,然而如果二分写bfs时间复杂度不够。可以用kruskal来求的最小生成树,最小生成树便是最小瓶颈树。此时两点路径上的最大值便是所求的答案。跑kruskal时候,当加入一条边,起点和终点联通的时候,这条边便是最大,也是答案。
uva10048
做完上一个题又想起前几天看到的uva10048,也是求两个点间的最大值最小的点,因为询问的数目比较多,紫书上给出的方法是写floyd,代码如下
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=100+10;
const int maxm=1000+100;
const int INF=2147483647;
int n,m,q,kase=0;
int G[maxn][maxn],d[maxn][maxn];
int main(){
//freopen("in.txt","r",stdin);
while(scanf("%d%d%d",&n,&m,&q)&&n&&m&&q){
if(kase)cout<<""<<endl;
kase++;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
G[i][j]=INF;
for(int i=1;i<=m;i++){
int a,b,c;
cin>>a>>b>>c;
G[a][b]=G[b][a]=c;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=G[i][j];
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
d[i][j]=min(d[i][j],max(d[i][k],d[k][j]));
}
}
cout<<"Case #"<<kase<<endl;
for(int i=1;i<=q;i++){
int a,b;
cin>>a>>b;
if(d[a][b]==INF)
cout<<"no path"<<endl;
else
cout<<d[a][b]<<endl;
}
}
return 0;
}
虽然询问的数目多,但是依然可以承受跑kruskal,
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int maxn=100+5;
const int maxm=1000+20;
struct Edge{
int u,v,w;
bool operator <(const Edge& rhs)const{
return w<rhs.w;
}
};
vector<Edge>edges;
int p[maxn];
int n,m,q,kase=0;
int find(int x){
return p[x]==x?x:p[x]=find(p[x]);
}
int main(){
// freopen("in.txt","r",stdin);
while(scanf("%d%d%d",&n,&m,&q)==3&&n&&m&&q){
kase++;
if(kase!=1)
cout<<""<<endl;
cout<<"Case #"<<kase<<endl;
edges.clear();
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
edges.push_back((Edge){u,v,w});
}
sort(edges.begin(),edges.end());
for(int i=1;i<=q;i++){
bool jud=0;
int a,b;
cin>>a>>b;
for(int i=1;i<=n;i++)p[i]=i;
for(int j=0;j<edges.size();j++){
int aa=find(edges[j].u),bb=find(edges[j].v);
if(aa!=bb)
p[aa]=bb;
if(find(a)==find(b)){
cout<<edges[j].w<<endl;
jud=1;
break;
}
}
if(!jud)
cout<<"no path"<<endl;
}
}
return 0;
}
当时一开始想写瓶颈树的时候,跑完最小生成树,想得到两个点间最长的边的时候,考虑了很多复杂的方法,用dfs得到路线然后再跑一边,代码又臭又长。