最小乘积生成树

  1 type
  2 ans=record
  3 p,q:longint;
  4 end;
  5 var
  6 s,u,v,x,y:array[0..10000] of longint;
  7 f:array[0..200] of longint;
  8 i,j,k,n:longint;
  9 anss,ans1,ans2:ans;
 10 o1,o2:int64;
 11 procedure swap(var x,y:longint);
 12 var
 13 w:longint;
 14 begin
 15     w:=x; x:=y; y:=w;
 16 end;
 17 procedure sort(l,r:longint);
 18 var
 19 w,mid,i,j:longint;
 20 begin
 21     i:=l; j:=r;
 22     mid:=s[(l+r) div 2];
 23     repeat
 24       while s[i]<mid do
 25         inc(i);
 26       while s[j]>mid do
 27         dec(j);
 28       if not(i>j) then
 29       begin
 30           swap(s[i],s[j]);
 31           swap(u[i],u[j]);
 32           swap(v[i],v[j]);
 33           swap(x[i],x[j]);
 34           swap(y[i],y[j]);
 35           inc(i);
 36           dec(j);
 37       end;
 38     until i>j;
 39     if l<j then sort(l,j);
 40     if i<r then sort(i,r);
 41 end;
 42 function find(x:longint):longint;
 43 begin
 44     if f[x]=x then exit(x);
 45     find:=find(f[x]);
 46     f[x]:=find;
 47 end;
 48 function kl:ans;
 49 var
 50 t,fx,fy,i:longint;
 51 sum:ans;
 52 begin
 53     sort(1,k);
 54     for i:=1 to n do f[i]:=i;
 55     sum.p:=0; sum.q:=0; t:=0;
 56     for i:=1 to k do
 57     begin
 58         fx:=find(x[i]);
 59         fy:=find(y[i]);
 60         if fx<>fy then
 61         begin
 62             f[fx]:=fy;
 63             inc(t);
 64             sum.p:=sum.p+u[i];
 65             sum.q:=sum.q+v[i];
 66             if t=n-1 then
 67             break;
 68         end;
 69     end;
 70     o1:=anss.p*anss.q;
 71     o2:=sum.p*sum.q;
 72     if (o1>o2)or((o1=o2)and(anss.p>sum.p)) then
 73     anss:=sum;
 74     kl:=sum;
 75 end;
 76 procedure solve(a,b:ans);
 77 var
 78 aa,bb,cc,i:longint;
 79 c:ans;
 80 begin
 81     aa:=(a.q-b.q);   bb:=(b.p-a.p);  cc:=a.p*b.q-a.q*b.p;
 82     for i:=1 to k do
 83         s[i]:=u[i]*aa+v[i]*bb;
 84     c:=kl;
 85     if c.p*aa+c.q*bb+cc>=0 then exit;
 86     solve(a,c);
 87     solve(c,b);
 88 end;
 89 begin
 90     read(n,k);
 91     for i:=1 to k do
 92     begin
 93         read(x[i],y[i],u[i],v[i]);
 94         inc(x[i]); inc(y[i]);
 95     end;
 96     anss.p:=trunc(sqrt(maxlongint)); anss.q:=trunc(sqrt(maxlongint));
 97     s:=u; ans1:=kl;
 98     s:=v; ans2:=kl;
 99     solve(ans1,ans2);
100     writeln(anss.p,' ',anss.q);
101 end.
102 
View Code

(bzoj 2395)

一般的,这种题型会给你两个参数a[i],b[i],先分别根据以a[i]与b[i]做最小生成树,得到最靠近X轴与Y轴的点。

因为题目要求∑a[i]*∑b[j]最小,也就是反比例函数y=k/x中的k最小,我们每次可以选择离直线最远的点。

但是我刚学向量不久,还不会叉积,于是只能用点到直线距离的公式。。。

每次求出两个边界点直线的解析式 ax+by+c=0;

由(x1,y1),(x2,y2) ;

可算出a=(y1-y2); b=(x2-x1); c=x1*y2-x2*y1;

直线到点(x,y)的距离为:a*x+b*y+c/√(a^2+b^2)

所以可以算出每个点到直线的距离大小又因为与点有关的是x,y,所以只要比较(a*x+b*y)的大小再进行排序

就求出了可能更优的边,再一直递归去求可能更优的点。

但递归边界是什么呢?

当求出的最优点在直线外侧时

  

很显然,当a*x+b*y+c>=0时

这一定没有A.B优,这时就可以退出了

posted @ 2017-03-27 15:37  jkl~  阅读(201)  评论(0编辑  收藏  举报