diff --git a/news/4.bugfix.rst b/news/4.bugfix.rst
new file mode 100644
index 0000000..071d5ae
--- /dev/null
+++ b/news/4.bugfix.rst
@@ -0,0 +1,3 @@
+Ensure the result returned by the resolver only contains candidates that are
+actually needed. This is done by tracing the graph after resolution completes,
+snipping nodes that don’t have a route to the root.
diff --git a/src/resolvelib/resolvers.py b/src/resolvelib/resolvers.py
index e733124..183b002 100644
--- a/src/resolvelib/resolvers.py
+++ b/src/resolvelib/resolvers.py
@@ -105,7 +105,7 @@ class ResolutionTooDeep(ResolutionError):
 
 
 # Resolution state in a round.
-State = collections.namedtuple("State", "mapping graph criteria")
+State = collections.namedtuple("State", "mapping criteria")
 
 
 class Resolution(object):
@@ -136,14 +136,10 @@ class Resolution(object):
         try:
             base = self._states[-1]
         except IndexError:
-            graph = DirectedGraph()
-            graph.add(None)  # Sentinel as root dependencies' parent.
-            state = State(mapping={}, graph=graph, criteria={})
+            state = State(mapping={}, criteria={})
         else:
             state = State(
-                mapping=base.mapping.copy(),
-                graph=base.graph.copy(),
-                criteria=base.criteria.copy(),
+                mapping=base.mapping.copy(), criteria=base.criteria.copy(),
             )
         self._states.append(state)
 
@@ -178,41 +174,16 @@ class Resolution(object):
 
     def _check_pinnability(self, candidate, dependencies):
         backup = self.state.criteria.copy()
-        contributed = set()
         try:
             for subdep in dependencies:
                 key = self._p.identify(subdep)
                 self._contribute_to_criteria(key, subdep, parent=candidate)
-                contributed.add(key)
         except RequirementsConflicted:
             criteria = self.state.criteria
             criteria.clear()
             criteria.update(backup)
-            return None
-        return contributed
-
-    def _pin_candidate(self, name, criterion, candidate, child_names):
-        try:
-            self.state.graph.remove(name)
-        except KeyError:
-            pass
-        self.state.mapping[name] = candidate
-        self.state.graph.add(name)
-        for parent in criterion.iter_parent():
-            parent_name = None if parent is None else self._p.identify(parent)
-            try:
-                self.state.graph.connect(parent_name, name)
-            except KeyError:
-                # Parent is not yet pinned. Skip now; this edge will be
-                # connected when the parent is being pinned.
-                pass
-        for child_name in child_names:
-            try:
-                self.state.graph.connect(name, child_name)
-            except KeyError:
-                # Child is not yet pinned. Skip now; this edge will be
-                # connected when the child is being pinned.
-                pass
+            return False
+        return True
 
     def _pin_criteria(self):
         criteria = self.state.criteria
@@ -233,15 +204,11 @@ class Resolution(object):
             if self._is_current_pin_satisfying(name, criterion):
                 # If the current pin already works, just use it.
                 continue
-            candidates = list(criterion.candidates)
-            while candidates:
-                candidate = candidates.pop()
+            for candidate in reversed(criterion.candidates):
                 dependencies = self._p.get_dependencies(candidate)
-                child_names = self._check_pinnability(candidate, dependencies)
-                if child_names is None:
-                    continue
-                self._pin_candidate(name, criterion, candidate, child_names)
-                break
+                if self._check_pinnability(candidate, dependencies):
+                    self.state.mapping[name] = candidate
+                    break
             else:
                 # All candidates tried, nothing works. This criterion is a dead
                 # end, signal for backtracking.
@@ -324,6 +291,56 @@ class Resolution(object):
         raise ResolutionTooDeep(max_rounds)
 
 
+def _has_route_to_root(criteria, key, all_keys, connected):
+    if key in connected:
+        return True
+    for p in criteria[key].iter_parent():
+        try:
+            pkey = all_keys[id(p)]
+        except KeyError:
+            continue
+        if pkey in connected:
+            connected.add(key)
+            return True
+        if _has_route_to_root(criteria, pkey, all_keys, connected):
+            connected.add(key)
+            return True
+    return False
+
+
+Result = collections.namedtuple("Result", "mapping graph criteria")
+
+
+def _build_result(state):
+    mapping = state.mapping
+    all_keys = {id(v): k for k, v in mapping.items()}
+    all_keys[id(None)] = None
+
+    graph = DirectedGraph()
+    graph.add(None)  # Sentinel as root dependencies' parent.
+
+    connected = {None}
+    for key, criterion in state.criteria.items():
+        if not _has_route_to_root(state.criteria, key, all_keys, connected):
+            continue
+        if key not in graph:
+            graph.add(key)
+        for p in criterion.iter_parent():
+            try:
+                pkey = all_keys[id(p)]
+            except KeyError:
+                continue
+            if pkey not in graph:
+                graph.add(pkey)
+            graph.connect(pkey, key)
+
+    return Result(
+        mapping={k: v for k, v in mapping.items() if k in connected},
+        graph=graph,
+        criteria=state.criteria,
+    )
+
+
 class Resolver(AbstractResolver):
     """The thing that performs the actual resolution work.
     """
@@ -358,4 +375,4 @@ class Resolver(AbstractResolver):
         """
         resolution = Resolution(self.provider, self.reporter)
         resolution.resolve(requirements, max_rounds=max_rounds)
-        return resolution.state
+        return _build_result(resolution.state)

diff --git a/tests/functional/cocoapods/test_resolvers_cocoapods.py b/tests/functional/cocoapods/test_resolvers_cocoapods.py
index 6e8b68b..428e17a 100644
--- a/tests/functional/cocoapods/test_resolvers_cocoapods.py
+++ b/tests/functional/cocoapods/test_resolvers_cocoapods.py
@@ -84,6 +84,10 @@ class CocoaPodsInputProvider(AbstractProvider):
             Requirement(key, _parse_specifier_set(spec))
             for key, spec in case_data["requested"].items()
         ]
+        self.preferred_versions = {
+            entry["name"]: packaging.version.parse(entry["version"])
+            for entry in case_data["base"]
+        }
         self.expected_resolution = dict(_iter_resolved(case_data["resolved"]))
         self.expected_conflicts = case_data["conflicts"]
 
@@ -111,9 +115,16 @@ class CocoaPodsInputProvider(AbstractProvider):
             yield Candidate(entry["name"], version, dependencies)
 
     def find_matches(self, requirement):
-        return sorted(
-            self._iter_matches(requirement), key=operator.attrgetter("ver"),
-        )
+        mapping = {c.ver: c for c in self._iter_matches(requirement)}
+        try:
+            version = self.preferred_versions[requirement.name]
+            preferred_candidate = mapping.pop(version)
+        except KeyError:
+            preferred_candidate = None
+        candidates = sorted(mapping.values(), key=operator.attrgetter("ver"))
+        if preferred_candidate:
+            candidates.append(preferred_candidate)
+        return candidates
 
     def is_satisfied_by(self, requirement, candidate):
         return candidate.ver in requirement.spec
@@ -123,6 +134,7 @@ class CocoaPodsInputProvider(AbstractProvider):
 
 
 XFAIL_CASES = {
+    "circular.json": "different resolution",
     "complex_conflict.json": "different resolution",
     "complex_conflict_unwinding.json": "different resolution",
     "conflict_on_child.json": "different resolution",
@@ -131,9 +143,7 @@ XFAIL_CASES = {
     "previous_conflict.json": "different resolution",
     "pruned_unresolved_orphan.json": "different resolution",
     "shared_parent_dependency_with_swapping.json": "KeyError: 'fog'",
-    "simple_with_base.json": "different resolution",
     "spapping_and_rewinding.json": "different resolution",
-    "swapping_children_with_successors.json": "different resolution",
 }
 
 
@@ -157,13 +167,10 @@ def test_resolver(provider, base_reporter):
     resolver = Resolver(provider, base_reporter)
     result = resolver.resolve(provider.root_requirements)
 
-    if provider.expected_conflicts:
-        return
-
     display = {
         identifier: str(candidate.ver)
         for identifier, candidate in result.mapping.items()
     }
     assert display == provider.expected_resolution
 
-    # TODO: Handle errors and assert conflicts.
+    # TODO: Assert conflicts.

Clean up graph after resolution
Resolver should clean up the graph to purge dead subtrees, and maping to remove unneeded pins after resolution (or maybe after each round?).
tests/functional/cocoapods/test_resolvers_cocoapods.py::test_resolver[swapping_children_with_successors]
tests/functional/cocoapods/test_resolvers_cocoapods.py::test_resolver[unresolvable_child] tests/functional/cocoapods/test_resolvers_cocoapods.py::test_resolver[swapping_changes_transitive_dependency] tests/functional/cocoapods/test_resolvers_cocoapods.py::test_resolver[previous_primary_conflict] tests/functional/cocoapods/test_resolvers_cocoapods.py::test_resolver[conflict] tests/functional/cocoapods/test_resolvers_cocoapods.py::test_resolver[contiguous_grouping] tests/functional/cocoapods/test_resolvers_cocoapods.py::test_resolver[simple_with_shared_dependencies] tests/functional/cocoapods/test_resolvers_cocoapods.py::test_resolver[simple_with_base] tests/functional/cocoapods/test_resolvers_cocoapods.py::test_resolver[root_conflict_on_child] tests/functional/cocoapods/test_resolvers_cocoapods.py::test_resolver[simple_with_dependencies] tests/functional/cocoapods/test_resolvers_cocoapods.py::test_resolver[simple] tests/functional/cocoapods/test_resolvers_cocoapods.py::test_resolver[three_way_conflict] tests/functional/cocoapods/test_resolvers_cocoapods.py::test_resolver[empty]