最小乘积生成树。
本质上是把生成树作为平面上的点,那么答案一定在下凸壳上。
递归求解。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxv 250 #define maxe 10050 #define inf 1000000007 using namespace std; int n,m,a,b,c,d,father[maxv]; struct edge { int x,y,a,b,c; edge (int x,int y,int a,int b):x(x),y(y),a(a),b(b) {} edge () {} }e[maxe]; struct point { int a,b; point (int a,int b):a(a),b(b) {} point () {} friend point operator - (const point & x,const point & y) { return point(x.a-y.a,x.b-y.b); } friend int operator * (const point & x,const point & y) { return x.a*y.b-x.b*y.a; } friend bool operator < (const point &x,const point & y) { long long ans1=(long long)x.a*x.b,ans2=(long long)y.a*y.b; return ans1==ans2?x.a<y.a:ans1<ans2; } }ans; bool cmp1(const edge & x,const edge & y) { if (x.a!=y.a) return x.a<y.a; return x.b<y.b; } bool cmp2(const edge & x,const edge & y) { if (x.b!=y.b) return x.b<y.b; return x.a<y.a; } bool cmp3(const edge & x,const edge & y) { if (x.c!=y.c) return x.c<y.c; else if (x.a!=y.a) return x.a<y.a; else return x.b<y.b; } int getfather(int x) { if (x!=father[x]) father[x]=getfather(father[x]); return father[x]; } point kruskal() { point ret=point(0,0); for (int i=1;i<=n;i++) father[i]=i; for (int i=1;i<=m;i++) { int x=e[i].x,y=e[i].y; int f1=getfather(x),f2=getfather(y); if (f1!=f2) {father[f1]=f2;ret.a+=e[i].a;ret.b+=e[i].b;} } if (ret<ans) ans=ret; return ret; } void min_product_tree(point A,point B) { point C; for (int i=1;i<=m;i++) e[i].c=(A.b-B.b)*e[i].a+(B.a-A.a)*e[i].b; sort(e+1,e+m+1,cmp3);C=kruskal(); if ((A-B)*(C-B)<=0) return; min_product_tree(A,C);min_product_tree(C,B); } int main() { ans=point(inf,inf); scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { scanf("%d%d%d%d",&a,&b,&c,&d);a++;b++; e[i]=edge(a,b,c,d); } point A,B,C; sort(e+1,e+m+1,cmp1);A=kruskal(); sort(e+1,e+m+1,cmp2);B=kruskal(); min_product_tree(A,B); printf("%d %d ",ans.a,ans.b); return 0; }