这里的所有其他解决方案都是指数级的——即使在不需要的情况下也是如此。这个问题表现出类似的子结构,因此应该用动态规划来解决。
您要做的是编写一个类来记忆子问题的解决方案:
class Counter {
struct Problem {
unordered_multiset<int> s;
vector<unordered_set<int>> v;
};
int Count(Problem const& p) {
if (m.v.size() == 0)
return 1;
if (m.find(p) != m.end())
return m[p];
// otherwise, attack the problem choosing either choosing an index 'i' (notes below)
// or a number 'n'. This code only illustrates choosing an index 'i'.
Problem smaller_p = p;
smaller_p.v.erase(v.begin() + i);
int retval = 0;
for (auto it = p.s.begin(); it != p.s.end(); ++it) {
if (smaller_p.s.find(*it) == smaller_p.s.end())
continue;
smaller_p.s.erase(*it);
retval += Count(smaller_p);
smaller_p.s.insert(*it);
}
m[p] = retval;
return retval;
}
unordered_map<Problem, int> m;
};
代码说明了选择索引 i,应该在有 v[i].size() 小的地方选择。另一种选择是选择一个数字 n,它应该是一个可以放置的位置 v 很少的数字。我想说两个决定因素中的最小值应该获胜。
此外,您必须为 Problem 定义一个哈希函数——使用 boost 的哈希值应该不会太难。
可以通过将向量替换为 set<> 并为 unordered_set 定义 < 运算符来改进此解决方案。这会将更多相同的子问题折叠到单个地图元素中,并进一步减少缓解指数爆炸。
可以通过制作相同的问题实例来进一步改进此解决方案,只是将数字重新排列为相同的值并比较为相同。