bzoj 1050
一条从S到T的路径的答案为MAX/MIN。
无法直接获得最优解。
所有边按边权从大到小排序,从大到小枚举MIN,要MAX尽量小。
问题变成给你i条无向边,使S与T联通且使边权的最大值最小。
这不就是最小生成树的性质吗?Kruskal算法。
从后往前枚举每一条边,如果边连接的两个节点不在同一集合,就合并,直到S与T在同一集合。
最小的MAX即是枚举到的最后一条边。
交叉相乘更新答案。用辗转相除法把答案变成既约分数。
#include<cstdio> #include<cctype> #include<algorithm> using namespace std; int read(){ char c; while(!isdigit(c=getchar())); int x=c-'0'; while(isdigit(c=getchar())) x=x*10+c-'0'; return x; } struct edge{ int u,v,d; }e[5001]; bool cmp(edge x,edge y){ return x.d>y.d; } int fa[501]; int find(int x){ return x==fa[x]? x:fa[x]=find(fa[x]); } int gcd(int x,int y){ return y==0? x:gcd(y,x%y); } int main(){ int n=read(),m=read(); for(int i=1;i<=m;i+=1) e[i].u=read(),e[i].v=read(),e[i].d=read(); sort(e+1,e+m+1,cmp); int s=read(),t=read(),res1=0,res2=0; for(int i=1;i<=m;i+=1){ int ans=0; for(int j=1;j<=n;j+=1) fa[j]=j; for(int j=i;j>=1;j-=1){ int x=find(e[j].u),y=find(e[j].v); if(x!=y) fa[x]=y; if(find(s)==find(t)){ ans=e[j].d; break; } } if(ans) if((!res1 && !res2) || res1*e[i].d>res2*ans){ int x=gcd(e[i].d,ans); res1=ans/x; res2=e[i].d/x; } } if(!res1 && !res2) puts("IMPOSSIBLE"); else if(res1%res2) printf("%d/%d",res1,res2); else printf("%d",res1/res2); return 0; }