|
158 | 158 | "QuantifiedBooleanFormulas": [Quantified Boolean Formulas (QBF)], |
159 | 159 | "RectilinearPictureCompression": [Rectilinear Picture Compression], |
160 | 160 | "ResourceConstrainedScheduling": [Resource Constrained Scheduling], |
| 161 | + "RootedTreeStorageAssignment": [Rooted Tree Storage Assignment], |
161 | 162 | "SchedulingWithIndividualDeadlines": [Scheduling With Individual Deadlines], |
162 | 163 | "SequencingToMinimizeMaximumCumulativeCost": [Sequencing to Minimize Maximum Cumulative Cost], |
163 | 164 | "SequencingToMinimizeWeightedCompletionTime": [Sequencing to Minimize Weighted Completion Time], |
@@ -2912,6 +2913,70 @@ A classical NP-complete problem from Garey and Johnson @garey1979[Ch.~3, p.~76], |
2912 | 2913 | ] |
2913 | 2914 | } |
2914 | 2915 |
|
| 2916 | +#{ |
| 2917 | + let x = load-model-example("RootedTreeStorageAssignment") |
| 2918 | + let n = x.instance.universe_size |
| 2919 | + let subsets = x.instance.subsets |
| 2920 | + let m = subsets.len() |
| 2921 | + let K = x.instance.bound |
| 2922 | + let config = x.optimal_config |
| 2923 | + let edges = config.enumerate().filter(((v, p)) => v != p).map(((v, p)) => (p, v)) |
| 2924 | + let fmt-set(s) = "${" + s.map(e => str(e)).join(", ") + "}$" |
| 2925 | + let highlight-nodes = (0, 2, 4) |
| 2926 | + let highlight-edges = ((0, 2), (2, 4)) |
| 2927 | + [ |
| 2928 | + #problem-def("RootedTreeStorageAssignment")[ |
| 2929 | + Given a finite set $X = {0, 1, dots, #(n - 1)}$, a collection $cal(C) = {X_1, dots, X_m}$ of subsets of $X$, and a nonnegative integer $K$, find a directed rooted tree $T = (X, A)$ and supersets $X_i' supset.eq X_i$ such that every $X_i'$ forms a directed path in $T$ and $sum_(i = 1)^m |X_i' backslash X_i| <= K$. |
| 2930 | + ][ |
| 2931 | + Rooted Tree Storage Assignment is the storage-and-retrieval problem SR5 in Garey and Johnson @garey1979. Their catalog credits a reduction from Rooted Tree Arrangement, framing the problem as hierarchical file organization: pick a rooted tree on the records so every request set can be completed to a single root-to-leaf path using only a limited number of extra records. The implementation here uses one parent variable per element of $X$, so the direct exhaustive bound is $|X|^(|X|)$ candidate parent arrays, filtered down to valid rooted trees#footnote[No exact algorithm improving on the direct parent-array search bound is claimed here for the general formulation.]. |
| 2932 | + |
| 2933 | + *Example.* Let $X = {0, 1, dots, #(n - 1)}$, $K = #K$, and $cal(C) = {#range(m).map(i => $X_#(i + 1)$).join(", ")}$ with #subsets.enumerate().map(((i, s)) => $X_#(i + 1) = #fmt-set(s)$).join(", "). The satisfying parent array $p = (#config.map(str).join(", "))$ encodes the rooted tree with arcs #edges.map(((u, v)) => $(#u, #v)$).join(", "). In this tree, $X_1 = {0, 2}$, $X_2 = {1, 3}$, and $X_4 = {2, 4}$ are already directed paths. The only extension is $X_3 = {0, 4}$, which becomes $X_3' = {0, 2, 4}$ along the path $0 -> 2 -> 4$, so the total extension cost is exactly $1 = K$. |
| 2934 | + |
| 2935 | + #pred-commands( |
| 2936 | + "pred create --example " + problem-spec(x) + " -o rooted-tree-storage-assignment.json", |
| 2937 | + "pred solve rooted-tree-storage-assignment.json --solver brute-force", |
| 2938 | + "pred evaluate rooted-tree-storage-assignment.json --config " + x.optimal_config.map(str).join(","), |
| 2939 | + ) |
| 2940 | + |
| 2941 | + #figure( |
| 2942 | + canvas(length: 1cm, { |
| 2943 | + import draw: * |
| 2944 | + |
| 2945 | + let positions = ( |
| 2946 | + (1.5, 1.8), |
| 2947 | + (0.6, 0.9), |
| 2948 | + (2.4, 0.9), |
| 2949 | + (0.6, 0.0), |
| 2950 | + (2.4, 0.0), |
| 2951 | + ) |
| 2952 | + |
| 2953 | + for (u, v) in edges { |
| 2954 | + let highlighted = highlight-edges.contains((u, v)) |
| 2955 | + line( |
| 2956 | + positions.at(u), |
| 2957 | + positions.at(v), |
| 2958 | + stroke: if highlighted { 1.2pt + graph-colors.at(0) } else { 0.8pt + luma(140) }, |
| 2959 | + mark: (end: "straight", scale: 0.45), |
| 2960 | + ) |
| 2961 | + } |
| 2962 | + |
| 2963 | + for (vertex, pos) in positions.enumerate() { |
| 2964 | + let highlighted = highlight-nodes.contains(vertex) |
| 2965 | + circle( |
| 2966 | + pos, |
| 2967 | + radius: 0.2, |
| 2968 | + fill: if highlighted { graph-colors.at(0) } else { white }, |
| 2969 | + stroke: 0.6pt + black, |
| 2970 | + ) |
| 2971 | + content(pos, if highlighted { text(fill: white)[$#vertex$] } else { [$#vertex$] }) |
| 2972 | + } |
| 2973 | + }), |
| 2974 | + caption: [Rooted Tree Storage Assignment example. The rooted tree encoded by $p = (#config.map(str).join(", "))$ is shown; the blue path $0 -> 2 -> 4$ is the unique extension needed to realize $X_3 = {0, 4}$ within total cost $K = #K$.], |
| 2975 | + ) <fig:rooted-tree-storage-assignment> |
| 2976 | + ] |
| 2977 | + ] |
| 2978 | +} |
| 2979 | + |
2915 | 2980 | #{ |
2916 | 2981 | let x = load-model-example("TwoDimensionalConsecutiveSets") |
2917 | 2982 | let n = x.instance.alphabet_size |
|
0 commit comments