[英]Partition Frisbees C++
我們在2D中有n個飛盤的集合F。 我們希望將F分為兩個子集F1和F2,以便每個子集中都沒有兩個飛盤相交。 我們的函數按以下方式輸入輸入:(x_j,y_j)是第j個飛盤的中心,而rad_j是第j個飛盤的半徑。 輸出應為s_0 s_1 ... s_n-1,如果第j個飛盤在F1中,則s_j = 1,如果第j個飛盤在F2中,則s_i = 2。 如果無法對F進行分區,則僅返回0。理想情況下,算法應以O(n ^ 2)時間計算。
我認為我應該使用某種類型的矩陣表示形式,例如圖形,但是我認為我不需要構造圖形,但是我認為BFS / DFS會很有用,但是我仍然堅持如何正是在O(n ^ 2)中完美地做到了這一點。 我正在用C ++編寫代碼。
您在圖形搜索的正確軌道上。 這是一個使用O(V + E)空間的C ++ 11,O(V ^ 2),深度優先搜索解決方案。
DFS本身在時間上為O(V + E),但是生成鄰接表的最明顯方式是O(V ^ 2)。
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
struct Frisbee
{
double x;
double y;
double radius;
};
int dfs(const vector< vector<int> > &adj, vector<int> &p, int last_ind, int curr_ind)
{
if (p[curr_ind]) // node already painted
{
if (p[last_ind] == p[curr_ind]) // painted same color as neighbor -> failure
return 0;
return 1; // painting is compatible
}
// node not yet painted
p[curr_ind] = (1 == p[last_ind] ? 2 : 1); // paint opposite color as neighbor
for (int j = 0; j < adj[curr_ind].size(); ++j)
if (!dfs(adj, p, curr_ind, adj[curr_ind][j])) // dfs on neighbors
return 0;
return 1;
}
int partition(const vector<Frisbee> &F, vector<int> &p)
{
// compute adjacency lists
vector< vector<int> > adj(F.size());
p.resize(F.size());
for (int i = 0; i < F.size(); ++i)
{
p[i] = 0;
for (int j = i + 1; j < F.size(); ++j)
{
double dist = sqrt((F[i].x - F[j].x) * (F[i].x - F[j].x) + (F[i].y - F[j].y) * (F[i].y - F[j].y));
if (dist < F[i].radius + F[j].radius)
{
adj[i].push_back(j);
adj[j].push_back(i);
}
}
}
// find starting points for dfs
for (int i = 0; i < F.size(); ++i)
if (0 == p[i]) // node i not yet painted
{
p[i] = 1; // arbitrarily choose initial color
for (int j = 0; j < adj[i].size(); ++j)
if (!dfs(adj, p, i, adj[i][j])) // dfs on neighbors
return 0;
}
return 1;
}
int main(int argc, char **argv)
{
vector<Frisbee> F = { { 1.0, 1.0, 1.0 }, { 2.0, 2.0, 1.0 }, { -1.0, -1.0, 1.0 }, { -2.0, -2.0, 1.0 }, { 5.0, 5.0, 1.0 }, { -5.0, 5.0, 1.0 } };
vector<int> p;
if (partition(F, p))
{
for (size_t i = 0; i < F.size(); ++i)
cout << p[i] << " ";
cout << endl;
}
else
cout << "No partition possible!" << endl;
F.push_back({ 1.5, 1.5, 1.0 }); // add a 3-way intersection
if (partition(F, p))
{
for (size_t i = 0; i < F.size(); ++i)
cout << p[i] << " ";
cout << endl;
}
else
cout << "No partition possible!" << endl;
return 0;
}
這是(在稍有不同的飛盤集合上的兩個分區的輸出):
1 2 1 2 1 1
No partition possible!
您不應該只是做一個堆疊的for循環並使用距離公式嗎? 即兩個相交,如果其中心之間的距離小於其半徑之和。
在那之后,您對它進行了狀態分解,然后可以繼續進行循環包含/排除(即包含所有內容並去除所有無效的內容,然后包含盡可能多的有效內容,等等)。
您可以在邊表示“接觸”的地方建立圖形。 然后,您可以在該圖上使用二分算法。 Boost.Graph包含一個。
http://www.boost.org/doc/libs/1_57_0/libs/graph/doc/is_bipartite.html
該算法為O(V + E),即,如果所有光盤彼此接觸,最壞的情況是O(V ^ 2)(盡管很有可能在這種情況下會中止)。
天真地構建圖形是O(V ^ 2),因為您必須對照所有其他磁盤檢查每個磁盤,盡管您可以通過構建地理四叉樹來首先對磁盤進行排序來優化常見情況。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.