1、衡阳市八中day2修正版solution第一题记得Mario 64上有一个游戏,古代背景,点击一个格子就会使得它周围3x3范围内的格子翻动,要求在3步之内把格子翻成上面的图案。此题类似,只不过是翻动本身和上下左右范围内的5个格,为怎样翻动才能翻成全为0的状态。要求输出翻动方式(1是翻动,0是不翻动)且按字典序排列最靠前的那个。首先注意字典序:记住,越靠上及越靠右的格子字典顺序越靠前。故第一个1越靠下,靠左最好。本题的搜索不太容易想到。如果直接暴搜,O(2(n2)的时间复杂度会算到地老天荒所以观察,可以发现,如果第一行的翻动方式确定了,就可以唯一确定第二行的翻动方式。原因是翻动过的第一行内,如果
2、是1的格子,下面第二行必须翻动,第一行如果为0,下面第二行必须不翻动。否则第一行的非0值就再也没有什么东西可以救它了。于是时间复杂度直降O(2n),可以搜索了。无需剪枝,125MS可以接受。第二题:The starting point here is a dynamic programming algorithm. Arbitrarily root the tree and consider cutting off a particular subtree in a soldering. This leaves one (or none if a wire was cut off at its
3、 endpoints) cut wire which extends out of the subtree to the parent and a set of wires that are wholly within the subtree. Now, note that all that is relevant is the length of the cut wire within the subtree and the total cost of all the other wires. This is because the cut wire is the only wire who
4、se cost depends on the rest of the soldering.This gives a relatively simple dynamic programming solution: for each vertex (defining a subtree) store, for each possible cut wire length, the minimum cost of the other wires; if there is no cut wire this can be taken as a wire of length 0. We will compu
5、te these from the bottom up. To compute these values, note that if there is a cut wire it must extend down to one of the children; the cost for a cut wire going through a particular child is the cost for the cut wire through the childs subtree plus the minimum cost soldering covering each of the oth
6、er subtrees. If there is no cut wire, then the edge going to the parent must be soldered onto the middle of another wire; then one can just check all pairs of lengths and distinct children to find two cut wires for two children to merge into into one wire. Now, note that the maximum length cut wire
7、for each subtree is the number of nodes it contains, so the number of pairs of lengths for any two distinct children is at most the number of pairs of nodes in the two children; summing over all children this is the number of nodes whose lowest common ancestor is the root of the subtree. Then the to
8、tal work done over the whole algorithm is only the total number of pairs of nodes, or O(N2).Now, at this point it will be convenient to assume, in the discussion of the algorithm, that each vertex has at most two children. In fact, this is not a problem: a vertex V with more children can be split up
9、 by giving it a direct edge to one of its children and attaching the remainder to a new vertex V with an edge to it from V of length 0 (the length does not break the algorithm although all edges in the problem were of length 1), then iterating this until no vertex has more than two children.To furth
10、er reduce the runtime, one must note the convexity properties of the squaring of the length. If one looks at a length/cost pair (l, c) for a subtree, it corresponds to the function (L+l)2+c where L is the length of the cut wire outside the subtree. But one only cares about those functions that are t
11、he minimum for some value of L: since (L+l)2+ c = L2+ 2L * l + (l2+ c), this is the lower envelope of these functions, equivalent to a convex hull. All pairs not in the envelope can be deleted. One can then binary search the convex hull to find the optimal pairing with any particular length of the w
12、ire outside the subtree. Then to find the optimal pair of lengths in the two children to merge into one wire, one can simply take all the lengths in the smaller subtree and binary search the convex hull in the larger subtree to find the best thing within that subtree to pair it with. Finally, to eff
13、iciently find the convex hulls for all subtrees, one can represent the convex hulls with binary search trees (std:set does fine here) and to get the possibilities from either child, one can offset the values in the larger child subtrees convex hull (by storing offset values that are added to all the
14、 pairs in the hull, since both length and cost change as you merge subtrees) and then insert each pair (offset) from the smaller subtree into it. The total number of operations on the binary search trees is then at most the sum of the sizes of the smaller child subtree from each node (in fact it can
15、 be smaller as the convex hull can have fewer elements than the size of the subtree). This can be shown to be O(N log N): one can consider the number of times each position gets merged into a larger group, and note that it is always less than log N since with each merge only the values in the smalle
16、r half are incremented. Each tree operation is O(log N), so the overall runtime is O(N log2N).Below is Neal Wus N2implementation:#include #include #include using namespace std;FILE *in = fopen (solder.in, r), *out = fopen (solder.out, w);const int MAXN = 50005;const long long LLINF = 1LL 60;int N, d
17、own MAXN;long long *dp MAXN, mindp MAXN;vector adj MAXN;void init_dfs (int num, int par) down num = 1; int par_ind = -1; for (int i = 0; i (int) adj num.size (); i+) int child = adj numi; if (child = par) par_ind = i; continue; init_dfs (child, num); down num = max (down num, down child + 1); if (pa
18、r_ind != -1) adj num.erase (adj num.begin () + par_ind);void solve_dfs (int num) for (int i = 0; i 1) for (int i = 0; i (int) adj num.size (); i+) dp1 += mindp adj numi; long long best_two = LLINF; for (int i = 0; i (int) adj num.size (); i+) for (int j = i + 1; j (int) adj num.size (); j+) int chil
19、d1 = adj numi, child2 = adj numj; for (int a = 1; a = down child1; a+) for (int b = 1; b = down child2; b+) best_two = min (best_two, dp child1a + dpchild2b + 2LL * a * b - mindp child1 - mindp child2); dp1 += best_two; dp num = new long long down num + 1; dp num1 = dp1; if (adj num.size () = 1) dp
20、num1 = LLINF; dp num0 = mindp adj num0; else dp num0 = dp num1 - 1; for (int k = 1; k down num; k+) long long sum = 0, best_link = LLINF; for (int i = 0; i (int) adj num.size (); i+) int child = adj numi; sum += mindp child; if (k = down child) best_link = min (best_link, dp childk - mindp child); d
21、p numk + 1 = sum + best_link + 2 * k + 1; mindp num = LLINF; for (int k = 1; k = down num; k+) mindp num = min (mindp num, dp numk); for (int i = 0; i (int) adj num.size (); i+) delete dp adj numi;int main () fscanf (in, %d, &N); for (int i = 1, a, b; i N; i+) fscanf (in, %d %d, &a, &b); a-; b-; adj
22、 a.push_back (b); adj b.push_back (a); init_dfs (0, -1); solve_dfs (0); fprintf (out, %lldn, dp 00); return 0;And below is Michael Cohens impressive full implementation:#include #include #include #define endl nusing namespace std;struct poss long long depth; long long cost; long long takeover; bool
23、tcheck;bool operator(poss a, poss b) if (a.tcheck | b.tcheck) return (a.takeover b.depth) return true; if (a.depth b.depth) return false; return (a.cost b.cost);int N;vector edges50000;bool visited50000;long long depth50000;long long offset50000;set* best50000;void recurse(int node) visitednode = tr
24、ue; long long bestPair = -1; long long allSoFar = 0; for (int i = 0; i edgesnode.size(); i+) if (visitededgesnodei) continue; depthedgesnodei = depthnode+1; recurse(edgesnodei); long long tadd; poss when = 0, 0, -depthnode, true ; set:iterator which = bestedgesnodei-upper_bound(when); which-; tadd =
25、(depthnode-which-depth)*(depthnode-which-depth)+which-cost+offsetedgesnodei; if (bestPair != -1) bestPair += tadd; if (bestnode = NULL) bestnode = bestedgesnodei; offsetnode = offsetedgesnodei; else set* s = bestnode, * t = bestedgesnodei; long long os = offsetnode+tadd, ot = offsetedgesnodei+allSoF
26、ar; if (s-size() size() set* tem = s; s = t; t = tem; int to = os; os = ot; ot = to; for (set:iterator it = t-begin(); it != t-end(); it+) poss when = 0, 0, it-depth-2*depthnode, true ; set:iterator which = s-upper_bound(when); which-; long long thisPair =(it-depth+which-depth-2*depthnode)*(it-depth
27、+which-depth-2*depthnode)+it-cost+which-cost+offsetnode+offsetedgesnodei; if (bestPair = -1 | thisPair bestPair) bestPair = thisPair; for (set:iterator it = t-begin(); it != t-end(); it+) poss p = *it; p.cost += ot-os; set:iterator where = s-insert(p).first; bool killed = false; while (where != s-be
28、gin() set:iterator prev = where; prev-; if (prev-depth = where-depth) s-erase(where); killed = true; break; p.takeover =(where-cost-prev-cost+where-depth*where-depth-prev-depth*prev-depth)/(2*prev-depth-2*where-depth); while (2*prev-depth-2*where-depth)*p.takeover cost-prev-cost+where-depth*where-depth-prev-depth*prev-depth)p.takeover+; s-erase(where); where = s-insert(p).first; if (where-takeover takeover) s-erase(prev); else break; if (killed) continue; if (where = s-begin() p.takeover = -1000000000; s-erase(where); where = s-insert(p).first; set:iter
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1