该题是最小生成树问题变通活用,表示自己开始没有想到该算法:先将所有边按权重排序,然后枚举最小边,求最小生成树(一个简单图的最小生成树的最大权是所有生成树中最大权最小的,这个容易理解,所以每次取最小边,求一次最小生成树,这样差值都次这次最小的),记录更新即可。并查集来判断连通。
类似一提,hoj1598,开始时用DFS搜索,TLE,受启发,用枚举方法差不多,只是在每次枚举最小边的时候结束条件改为起点与终点连通,连通就结束(father(start)==father(end))。
#include<iostream> //poj 3532 219MS #include<cstring> //最小生成树有一个很重要的性质:在构造生成树时有可能选择不同的边,但最小生成树的权是唯一的!所以在用kruskal算法时第一次加入的必然是最小生成树的最小边权值,最小边确定后,最小生成树的最大边的权值是所以生成树中最小的,于是只要枚举最小边,然后求最小生成树,就可以得到最大边,只要每次更新最优解就行了。 #include<cstdio> #include<vector> #include<algorithm> using namespace std; struct edge //边 { int pre; //前一个点//一边的俩端点 int to; //后一个点 int w; }; int best;int curmin,curmax;int fa[202];int flag=0; bool my(const edge &a,const edge & b ) { return a.w<b.w; } int father(int x) { return (x==fa[x]?x:father(fa[x])); } int main() { int n,m; while(~scanf("%d%d",&n,&m)&&(m+n)) { vector<edge>v(m); int s,l,w; for(int i=0;i<m;i++) { scanf("%d%d%d",&s,&l,&w); v[i].to=l; v[i].w=w; v[i].pre=s; } sort(v.begin(),v.end(),my); //排序 best=1000001;flag=0; for(int i=0;i<m;i++) //按从小到大枚举边 { for(int ii=0;ii<=n;ii++) //初始化并查集 fa[ii]=ii; int num=0; curmin=v[i].w; curmax=-1; for(int k=i;k<m;k++) //并查集判断联通 { int xx=father(v[k].pre); int yy=father(v[k].to); if(xx!=yy) //连通性判断 { if(curmax<v[k].w) curmax=v[k].w; if(curmax-curmin>best)break; //无此剪枝3000MS。 fa[xx]=yy; num++; } } if(i==0&&num!=n-1){flag=1;break;} if(num==n-1&&best>curmax-curmin) best=curmax-curmin; } if(flag||m==0){printf("-1 ");continue;} else printf("%d ",best); } return 0; }
#include<iostream> //100+MS #include<cstring> //最小生成树有一个很重要的性质:在构造生成树时有可能选择不同的边,但最小生成树的权是唯一的!所以在用kruskal算法时第一次加入的必然是最小生成树的最小边权值,最小边确定后,最小生成树的最大边的权值是所以生成树中最小的,于是只要枚举最小边,然后求最小生成树,就可以得到最大边,只要每次更新最优解就行了。 #include<cstdio> #include<vector> #include<algorithm> using namespace std; struct edge //边 { int pre; //前一个点//一边的俩个端点 int to; //后一个点 int w; }; int best;int curmin,curmax;int fa[202];int flag=0; bool my(const edge &a,const edge & b ) { return a.w<b.w; } int father(int x) { return (x==fa[x]?x:father(fa[x])); } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { vector<edge>v(m); int s,l,w; for(int i=0;i<m;i++) { scanf("%d%d%d",&s,&l,&w); v[i].to=l; v[i].w=w; v[i].pre=s; } sort(v.begin(),v.end(),my); //排序 int q;scanf("%d",&q); while(q--) { int start,end; scanf("%d%d",&start,&end); best=1000001; for(int i=0;i<m;i++) //按从小到大枚举边 { flag=0; for(int ii=0;ii<=n;ii++) //初始化并查集 fa[ii]=ii; curmin=v[i].w; curmax=-1; for(int k=i;k<m;k++) //并查集判断联通 { int xx=father(v[k].pre); int yy=father(v[k].to); if(xx!=yy) //连通性判断 { if(curmax<v[k].w) curmax=v[k].w; if(curmax-curmin>best)break; //无此剪枝300+MS。 fa[xx]=yy; } if(father(start)==father(end)){ flag=1;break;} //联通则退出 } if(flag==0&&i==0){break;} if(flag==1&&best>curmax-curmin) best=curmax-curmin; } if(best==1000001||m==0){printf("-1 ");continue;} else printf("%d ",best); } } return 0; }
#include<iostream> //搜索TLE #include<cstring> #include<cstdio> #include<vector> using namespace std; struct edge { int pre; int to; int w; }; int head[202];int mark[202];int best;int curmin,curmax; int flag=0; void dfs(int end,vector<edge>v,int cur,int shendu,int n) { if(best==0)return; if(flag)return; if(shendu==n){flag=1;return;} if(curmax-curmin>=0&&curmax-curmin>=best)return; if(cur==end) { if(curmax-curmin<best) best=curmax-curmin; return; } for(int i=head[cur];i!=-1;i=v[i].pre) if(mark[v[i].to]==0) { int tempcurmin=curmin;int tempcurmax=curmax; if(v[i].w<curmin)curmin=v[i].w; if(v[i].w>curmax)curmax=v[i].w; mark[v[i].to]=1; dfs(end,v,v[i].to,shendu+1,n); curmin=tempcurmin; curmax=tempcurmax; mark[v[i].to]=0; } return ; } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { vector<edge>v(2*m); int s,l,w; memset(head,-1,sizeof(head)); for(int i=0;i<2*m;i++) { scanf("%d%d%d",&s,&l,&w); v[i].to=l;v[i].w=w; v[i].pre=head[s]; head[s]=i; i++; v[i].to=s;v[i].w=w; v[i].pre=head[l]; head[l]=i; } int q;scanf("%d",&q); int start,end; while(q--) { scanf("%d%d",&start,&end); memset(mark,0,sizeof(mark)); flag=0; best=1000001;curmin=1000001;curmax=-1; mark[start]=1; dfs(end,v,start,1,n); if(best==1000001)printf("-1 "); else printf("%d ",best); } } return 0; }