這道題第一次做以為是一般的回溯題,最初的思路就是將各圓形全排列,放置新圓的時候讓其與前一個圓相切,最後通過回溯得到矩形的最小size。十幾分鐘編完後結果WA,想了好一會發現問題所在,不能只讓其與前一個圓相切(如果第一個圓很大,半徑比方說是100,第二個圓很小,半徑是1,第三個圓也很大,半徑同樣是100,放第三個圓的時候如果是和第二個圓相切則必定會與第一個圓相交,就不可以了)。然後就開始修改代碼,然後就遇到了各種錯誤,斷斷續續地弄了一晚上加一上午。 首先想的是每次放一個圓形的時候還是先讓其與前一個放置的圓相切,然後判斷它與再之前放的圓是否相交,如不相交,則說明這樣放是正確且最節省距離的,如果有任何一個圓與其相交,則說明不能與前一個圓相切,就再往前推一個,讓其與前前個圓相切,再判斷是否和別的圓相交。。。為了判斷是否相交,增加了一個center數組儲存各圓的圓心位置。到此思路很正確很清晰,然後遇到了三個Wa的點。 WA1:判斷矩形最小size的時候不能用最後一個圓的最右側位置了,因為有可能最後一個圓很小,其最右側的位置還不如其前一個圓的最右側位置遠,所以改用判斷各圓圓心位置+半徑的最大值作為矩形的最小size。 WA2:在放置前幾個圓的時候注意不能讓圓的圓心位置-圓的半徑<0。比如說第一個圓半徑是1,第二個元半徑100,放第二個元就不能讓其與第一個圓相切因為那樣圓的左邊就超出矩形盒子的壁了! WA3:主函數中MinL設定的太小,估計測試資料的數有很大的,題目沒有說半徑最大是多少,開始設的是65536,結果WA,改成DBL_MAX就AC了!!
#include<iostream> #include<cmath> #include<iomanip> #include<cstring> #include<float.h> using namespace std; int m,Put[10]; //Put[i]:放置的第i個圓的編號 double MinL,size[10],center[10];//size[i]:編號為i的圓的半徑;center[i]:放置的第i個圓的圓心位置 bool vis[10]; double getlen(int a,int b) //計算兩圓心間的距離 { return sqrt((size[a]+size[b])*(size[a]+size[b])-(size[a]-size[b])*(size[a]-size[b])); } bool isok(int a,int b) //是否和之前放的圓相交 { for (int i=0;i<b;i++) { if (sqrt((center[i]-center[a])*(center[i]-center[a])+(size[Put[i]]-size[Put[a]])*(size[Put[i]]-size[Put[a]]))<(size[Put[i]]+size[Put[a]])) return false; } return true; } void dfs(int cur) { int i,j; if (cur==m) { double maxsize=0; for (int k=0;k<cur;k++) { if (center[k]+size[Put[k]]>maxsize) maxsize=center[k]+size[Put[k]]; } if (maxsize<MinL) MinL=maxsize; } else { for (i=0;i<m;i++) { if (!vis[i]) { Put[cur]=i; vis[i]=1; double tmpl; bool ok=1; for (j=cur-1;j>=0;j--) { tmpl=getlen(Put[j],i); center[cur]=center[j]+tmpl; if (center[cur]-size[Put[cur]]<0) {ok=0;break;} if (isok(cur,j)) break; } if (ok) dfs(cur+1); vis[i]=0; } } } } int main() { int n; cin>>n; while(n--) { cin>>m; MinL=DBL_MAX; memset(vis,0,sizeof(vis)); for (int i=0;i<m;i++) cin>>size[i]; for (int i=0;i<m;i++) { Put[0]=i; center[0]=size[i]; vis[i]=1; dfs(1); vis[i]=0; } cout<<fixed<<setprecision(3)<<MinL<<endl; } return 0; }