感觉网上很多题解写的似乎不清楚,这里说一下我的思路
显然对于每个用户的材料(设其比例为Ai,Bi,Ci),
我们要么最多用3种原料(设其比例为ai,bi,ci)混合成需要材料,要么一定混合不成,具体原因往下看
我们设这3种原料所取比例为x1,x2,x3,可得
x1*a1+x2*a2+x3*a3=Ai
x1*b1+x2*b2+x3*b3=Bi
x1*(1-a1-b1)+x2*(1-a2-b2)+x3*(1-a3-b3)=1-Ai-Bi
整理第三个式子可得x1+x2+x3=1 x3=1-x1-x2 (注意这里x1,x2,x3>=0)
带回到前两个式子可得
x1*a1+x2*a2+(1-x1-x2)*a3=Ai
x1*b1+x2*b2+(1-x1-x2)*b3=Bi
整理可得
x1*(a1-a3)+x2*(a2-a3)=Ai-a3
x1*(b1-b3)+x2*(b2-b3)=Bi-b3
看到这个式子大家仔细想就会发现,这其实就是:
向量(a1-a3,b1-b3)和(a2-a3,b2-b3)能否表示向量(Ai-a3,Bi-b3)
根据平面向量的理论我们知道上面的结论是正确的
学过向量的同学可能还会听过这样一个结论(很好证)
设OABC四点,B在线段AC上,O不与ABC共线
则向量OB=a向量OA+b向量OC 一定满足a+b=1 而这里我们的要求也是x1+x2<=1
也就是如果把ai,bi和A,B看做材料点(ai,bi)和原料点(Ai,Bi)
如果用户所需材料i能被原料融合,当且仅当存在三个原料点构成的三角形能把材料点围起来
于是下面的思路就很明显了,我们先求出原料点所组成的凸包,如果所有用户都在凸包内则有解否则无解
接下来我们要找的是最少凸包上的点组成的多边形就能把所有原料点包含了
首先我们要理解什么是包含,我们从凸包一点出发,沿着下凸线到上凸线的顺序走回起点
这样凸包上的每条边就是一个向量,包含的意思就是:每个原料点都必须在每个向量的左边(叉积判断)
于是这就好办了,于是我们穷举向量的起点i终点j,如果所有原料点都在这个向量的左边那么d[i,j]=1 否则d[i,j]=inf
下面我们只要求出一个最小环即可,这可以用floyd搞定
注意这里凸包上是不能存在三点共线的
比较恶心的是这道题要注意精度问题
1 const eps=1e-10; 2 type point=record 3 x,y:double; 4 end; 5 6 var a,b:array[0..510] of point; 7 w:array[0..510] of longint; 8 f:array[0..510,0..510] of longint; 9 ans,i,j,k,p,n,m:longint; 10 ch:boolean; 11 12 function min(a,b:longint):longint; 13 begin 14 if a>b then exit(b) else exit(a); 15 end; 16 17 procedure swap(var a,b:point); 18 var c:point; 19 begin 20 c:=a; 21 a:=b; 22 b:=c; 23 end; 24 25 26 procedure sort(l,r:longint); 27 var i,j:longint; 28 p:point; 29 begin 30 i:=l; 31 j:=r; 32 p:=a[(l+r) shr 1]; 33 repeat 34 while (a[i].x<p.x) or (a[i].x=p.x) and (a[i].y<p.y) do inc(i); 35 while (p.x<a[j].x) or (a[j].x=p.x) and (p.y<a[j].y) do dec(j); 36 if not(i>j) then 37 begin 38 swap(a[i],a[j]); 39 inc(i); 40 dec(j); 41 end; 42 until i>j; 43 if l<j then sort(l,j); 44 if i<r then sort(i,r); 45 end; 46 47 function cross(a,b,c:point):double; 48 begin 49 exit((b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y)); 50 end; 51 52 function check:boolean; 53 var i,j:longint; 54 begin 55 dec(k); 56 for i:=1 to k do 57 for j:=1 to n do 58 if cross(a[w[i]],a[w[i+1]],b[j])<-eps then exit(false); 59 exit(true); 60 end; 61 62 begin 63 readln(m,n); 64 for i:=1 to m do 65 readln(a[i].x,a[i].x,a[i].y); 66 for i:=1 to n do 67 readln(b[i].x,b[i].x,b[i].y); 68 if n=0 then 69 begin 70 writeln(0); 71 halt; 72 end; 73 for i:=1 to m do 74 begin 75 ch:=true; 76 for j:=1 to n do 77 if (a[i].x<>b[j].x) or (a[i].y<>b[j].y) then 78 begin 79 ch:=false; 80 break; 81 end; 82 if ch then //这里特判一下 83 begin 84 writeln(1); 85 halt; 86 end; 87 end; 88 sort(1,m); 89 k:=1; 90 w[1]:=1; 91 for i:=2 to m do 92 begin 93 while (k>1) and (cross(a[w[k-1]],a[w[k]],a[i])<eps) do dec(k); 94 inc(k); w[k]:=i; 95 end; 96 j:=k; 97 for i:=m-1 downto 1 do 98 begin 99 while (k>j) and (cross(a[w[k-1]],a[w[k]],a[i])<eps) do dec(k); 100 inc(k); w[k]:=i; 101 end; 102 if check then 103 begin 104 for i:=1 to k do 105 for j:=1 to k do 106 begin 107 f[i,j]:=m; 108 if i<>j then 109 begin 110 ch:=true; 111 for p:=1 to n do 112 if cross(a[w[i]],a[w[j]],b[p])<-eps then 113 begin 114 ch:=false; 115 break; 116 end; 117 if ch then f[i,j]:=1; 118 end; 119 end; 120 121 for p:=1 to k do 122 for i:=1 to k do 123 for j:=1 to k do 124 f[i,j]:=min(f[i,j],f[i,p]+f[p,j]); 125 ans:=m; 126 for i:=1 to k do 127 ans:=min(ans,f[i,i]); 128 writeln(ans); 129 end 130 else writeln(-1); 131 end.