TopCoder SRM 622 Div1 Medium Ethernet
問題
n頂点からなる無向木が与えられる。
この木の辺を何本か切断して、直径がそれぞれmaxDist以下であるような木いくつかにしたい。
最小でいくつの木に分ければよいか求めよ。
制約条件
n≦51
辺の長さ≦500
maxDist≦500
方針
木DP.
vを根とする部分木で、深さdまで子を伸ばしていいときの最小の切断回数をrec(v, d)とする。
これを再帰していけばよい。
一番深いところで切断する子idxを選び、そのときの深さmdを全通り試す。
一番深いとこで切断する子以外の子において切断する深さは、
md以下かつ、maxDist - md以下であれば条件を満たす。
子につながる辺を切断した場合は、次のd = maxDistとして再帰すればよい。
で、最悪計算量が上手く見積もれないのだけど、
実質回るところはあまりなくて、最悪150msくらいで通る。
ソースコード
class Ethernet { public: int n, maxDist; int dp[51][501]; vector<vector<pi> > e; int rec(int c, int d){ if(d < 0) return 10000; int &res = dp[c][d]; if(res >= 0) return res; if(e[c].empty()) return res = 0; res = inf; rep(idx, e[c].size()) for(int md = 0; md <= d; md++){ int tmp = 0; rep(i, e[c].size()){ int lim = i == idx ? md : min(maxDist - md, md); tmp += min(rec(e[c][i].first, lim - e[c][i].second), rec(e[c][i].first, maxDist) + 1); } res = min(res, tmp); } return res; } int connect(vector <int> parent, vector <int> dist, int maxDist) { memset(dp, -1, sizeof(dp)); this->maxDist = maxDist; n = parent.size() + 1; e.clear(); e.resize(n); rep(i, n - 1) e[parent[i]].pb(mp(i + 1, dist[i])); return rec(0, maxDist) + 1; } };