Python、 1,291 1,271 1,225バイト
マーティンが指摘したように、この問題は、彼の優れたラバーバンドの課題に大きく帰着することができます。その課題の用語を使用して、2番目の釘のセットとして、囲まれた領域の境界上の円間の交点を取得できます。
ラバーバンドとして、囲まれた領域内で実行される2つのエンドポイント間の任意のパスPを使用できます。次に、ラバーバンド問題の解決策を呼び出して、(局所的に)最小のパスを作成できます。もちろん、そのようなパスPを見つけるか、より正確には、少なくとも1つがグローバルに最小のパスを生成できるように十分なパスを見つけることが課題です(最初のテストケースでは、少なくとも1つのパスが必要ですすべての可能性をカバーし、2番目のテストケースでは少なくとも2つ。)
素朴なアプローチは、すべての可能なパスを試すことです:2つの端点を接続する隣接する(つまり交差する)円のシーケンスごとに、中心に沿ってパスを取ります(2つの円が交差する場合、中心間のセグメントは常に結合内にあります) 。)このアプローチは技術的には正しいものの、途方もなく多くのパスにつながる可能性があります。私はこのアプローチを使用して最初のテストケースを数秒で解決できましたが、2番目のテストケースは永遠にかかりました。それでも、この方法を出発点として、テストする必要があるパスの数を最小限に抑えることができます。これは次のとおりです。
基本的に円グラフ上で深さ優先検索を実行することにより、パスを構築します。検索の各ステップで潜在的な検索方向を排除する方法を探しています。
ある時点で、互いに隣接している2つの隣接する円BとCがある円Aにいると仮定します。BにアクセスするとAからCに移動できます(逆も同様です)。したがって、AからBとCの両方に直接アクセスする必要はないと考えるかもしれません。残念ながら、この図が示すように、これは間違っています。
図のポイントが2つのエンドポイントである場合、Bを介してAからCに移動することにより、より長いパスが得られることがわかります。
これについてはどうでしょう:移動A → BとA → Cの両方をテストする場合、A → B → CまたはA → C → Bをテストする必要はありません。また違う:
ポイントは、純粋に隣接関係に基づいた引数を使用しても、それが削減されないということです。問題のジオメトリも使用する必要があります。上記の2つの例の共通点(および大規模な2番目のテストケース)は、囲まれた領域に「穴」があることです。それは、境界上の交点のいくつか、つまり「爪」が、頂点が円の中心である三角形△ ABCの中にあるという事実に現れています。
これが発生すると、AからBに、そしてAからCに行くと、異なるパスになる可能性があります。さらに重要なことは、それが起こらない場合(つまり、A、B、およびCの間にギャップがなかった場合)、A → B → CおよびA → Cで始まり、それ以外の場合は同等であるすべてのパスが結果として保証されることです同じローカル最小パスで、したがってBにアクセスする場合、Aから直接Cにアクセスする必要はありません。
これは、私たちの除去方法につながります。サークルAにいるとき、私たちが訪れた隣接するサークルのリストHを保持します。このリストは最初は空です。A、Bの中心とBに隣接するHの円のいずれかによって形成されるすべての三角形に「爪」がある場合、隣接する円Bを訪問します。この方法では、テストする必要があるパスの数が最初のテストケースで1つ、2番目のテストケースで10に劇的に減少します。
さらにいくつかの注意事項:
テストするパスの数をさらに減らすことは可能ですが、この方法はこの問題の規模に十分対応できます。
ラバーバンドチャレンジのソリューションからアルゴリズムを使用しました。このアルゴリズムはインクリメンタルであるため、パス検索プロセスに非常に簡単に統合できるため、進行中にパスを最小化できます。多くのパスが開始セグメントを共有するため、多くのパスがある場合、これによりパフォーマンスが大幅に向上します。また、有効なパスよりも多くの行き止まりがある場合、パフォーマンスを損なう可能性があります。いずれにしても、与えられたテストケースでは、各パスのアルゴリズムを個別に実行するだけで十分です。
このソリューションが失敗する可能性のあるエッジケースが1つあります。境界上の点のいずれかが2つの接線円の交点である場合、ある条件下では結果が間違っている可能性があります。これは、ラバーバンドアルゴリズムの動作方法によるものです。いくつかの変更を加えることで、これらのケースを処理することもできますが、それはすでに十分な長さです。
# First test case
I={((32.,42.),64.),((112.,99.),59.),((141.,171.),34.),((157.,191.),28.),((177.,187.),35.),((244.,168.),57.),((289.,119.),20.),((299.,112.),27.),((354.,59.),58.),((402.,98.),23.),((429.,96.),29.),((424.,145.),34.),((435.,146.),20.),((455.,204.),57.),((430.,283.),37.),((432.,306.),48.),((445.,349.),52.),((424.,409.),59.),((507.,468.),64.)}
# Second test case
#I={((32.,42.),64.),((112.,99.),59.),((141.,171.),34.),((157.,191.),28.),((177.,187.),35.),((244.,168.),57.),((289.,119.),20.),((299.,112.),27.),((354.,59.),58.),((402.,98.),23.),((429.,96.),29.),((424.,145.),34.),((435.,146.),20.),((455.,204.),57.),((430.,283.),37.),((432.,306.),48.),((445.,349.),52.),((424.,409.),59.),((507.,468.),64.),((180.,230.),39.),((162.,231.),39.),((157.,281.),23.),((189.,301.),53.),((216.,308.),27.),((213.,317.),35.),((219.,362.),61.),((242.,365.),42.),((288.,374.),64.),((314.,390.),53.),((378.,377.),30.),((393.,386.),34.)}
from numpy import*
V=array;X=lambda u,v:u[0]*v[1]-u[1]*v[0];L=lambda v:dot(v,v)
e=V([511]*2)
N=set()
for c,r in I:
for C,R in I:
v=V(C)-c;d=L(v)
if d:
a=(r*r-R*R+d)/2/d;q=r*r/d-a*a
if q>=0:w=V(c)+a*v;W=V([-v[1],v[0]])*q**.5;N|={tuple(t)for t in[w+W,w-W]if all([L(t-T)>=s**2-1e-9 for T,s in I])}
N=map(V,N)
def T(a,b,c,p):H=[X(p-a,b-a),X(p-b,c-b),X(p-c,a-c)];return min(H)*max(H)>=0
def E(a,c,b):
try:d=max((X(n-a,b-a)**2,id(n),n)for n in N if T(a,b,c,n)*X(n-b,c-b)*X(n-c,a-c))[2];A=E(a,c,d);B=E(d,c,b);return[A[0]+[d]+B[0],A[1]+[sign(X(c-a,b-c))]+B[1]]
except:return[[]]*2
def P(I,c,r,A):
H=[];M=[""]
if L(c-e)>r*r:
for C,R in I:
if L(C-c)<=L(r+R)and all([L(h-C)>L(R+s)or any([T(c,C,h,p)for p in N])for h,s in H]):v=V(C);H+=[(v,R)];M=min(M,P(I-{(C,R)},v,R,A+[v]))
return M
A+=[e]*2;W=[.5]*len(A)
try:
while 1:
i=[w%1*2or w==0for w in W[2:-2]].index(1);u,a,c,b,v=A[i:i+5];A[i+2:i+3],W[i+2:i+3]=t,_=E(a,c,b);t=[a]+t+[b]
for p,q,j,k in(u,a,1,i+1),(v,b,-2,i+len(t)):x=X(q-p,c-q);y=X(q-p,t[j]-q);z=X(c-q,t[j]-q);d=sign(j*z);W[k]+=(x*y<=0)*(x*z<0 or y*z>0)*(x!=0 or d*W[k]<=0)*(y!=0 or d*W[k]>=0)*d
except:return[sum(L(A[i+1]-A[i])**.5for i in range(len(A)-1)),id(A),A]
print V(P(I,e*0,0,[e*0]*2)[2][1:-1])
入力変数を介して与えられるI
タプルの集合として円の中心であり、その半径です。値はsではなくsでなければなりません。結果はSTDOUTに出力されます。((x, y), r)
(x, y)
r
float
int
例
# First test case
I={((32.,42.),64.),((112.,99.),59.),((141.,171.),34.),((157.,191.),28.),((177.,187.),35.),((244.,168.),57.),((289.,119.),20.),((299.,112.),27.),((354.,59.),58.),((402.,98.),23.),((429.,96.),29.),((424.,145.),34.),((435.,146.),20.),((455.,204.),57.),((430.,283.),37.),((432.,306.),48.),((445.,349.),52.),((424.,409.),59.),((507.,468.),64.)}
[[ 0. 0. ]
[ 154.58723733 139.8329183 ]
[ 169.69950891 152.76985495]
[ 188.7391093 154.02738541]
[ 325.90536774 109.74141936]
[ 382.19108518 109.68789517]
[ 400.00362897 120.91319495]
[ 511. 511. ]]