问题描述
示例:
5
12 34 10 7 8
3
1 2
2 3
3 1
输出:
56
- 组形成为:[1,2,3][4][5]。
- 最多收集 56 个(由志愿者 1、2 和 3 收集)。
我的逻辑是运行 dfs 并将图中所有组件的最大值存储为我的 ans,
这是我的代码:
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
int dfs(int v,int values[],unordered_map<int,vector<int>> adj,int visited[])
{
int ans = 0;
if (visited[v] == 0)
{
visited[v] = 1;
for (auto j : adj[v])
{
if (visited[j] == 0)
{
ans = ans + dfs(j,values,adj,visited);
}
}
return ans + values[v];
}
return 0;
}
void solve()
{
int n;
cin >> n;
int v[n];
int visited[n];
for (int i = 0; i < n; i++)
{
visited[i] = 0;
}
//vector<vector<int>> adj(n);
unordered_map<int,vector<int>> adj;
for (int i = 0; i < n; i++)
{
cin >> v[i];
}
int p;
int ans = 0;
cin >> p;
while (p--)
{
int f,s;
cin >> f >> s;
f = f - 1;
s = s - 1;
adj[f].push_back(s);
adj[s].push_back(f);
}
for (int i = 0; i < n; i++)
{
if (visited[i] == 0)
{
int val = dfs(i,v,visited);
if (val > ans)
{
ans = val;
}
}
}
// for (int i = 0; i < n; i++)
// {
// cout << i << " ";
// for (auto j : adj[i])
// {
// cout << j << " ";
// }
// cout << endl;
// }
cout << ans << endl;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
// #ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
// #endif // ONLINE_JUDGE
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
}
但是我的解决方案给出了 memory limited exceeded
错误。
然后我尝试使用 union-find
数据结构,但它给出了 wrong answer
。这是我的代码:
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
vector<int> parent;
vector<int> values;
vector<int> r;
int find(int s)
{
if (parent[s] == s)
{
return s;
}
return parent[s] = find(parent[s]);
}
void unite(int a,int b)
{
a = find(a);
b = find(b);
if (a != b)
{
if (r[a] < r[b])
{
parent[a] = parent[b];
r[b] = r[b] + r[a];
}
else
{
parent[b] = parent[a];
r[a] = r[a] + r[b];
}
}
}
void solve()
{
int n;
cin >> n;
parent.resize(n);
values.resize(n);
r.resize(n);
int ans = 0;
for (int i = 0; i < n; i++)
{
parent[i] = i;
}
for (int i = 0; i < n; i++)
{
cin >> values[i];
}
for (int i = 0; i < n; i++)
{
r[i] = 1;
}
int p;
cin >> p;
unordered_map<int,int> m;
while (p--)
{
int f,s;
cin >> f >> s;
f = f - 1;
s = s - 1;
int f1 = find(f);
int f2 = find(s);
if (f1 != f2)
{
unite(f1,f2);
}
}
for (int i = 0; i < n; i++)
{
if (m[parent[i]])
{
m[parent[i]] = m[parent[i]] + values[i];
ans = max(ans,m[parent[i]]);
}
else
{
m[parent[i]] = values[i];
ans = max(ans,m[parent[i]]);
}
}
cout << ans << endl;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
#ifndef ONLINE_JUDGE
freopen("input.txt",stdin);
freopen("output.txt",stdout);
#endif // ONLINE_JUDGE
solve();
}
有人可以指导我哪里出错了吗?
解决方法
很遗憾,我无法遵循您的方法。对此我深感抱歉。
无论如何,我建议使用基于“循环排列”的不同算法。手头的问题要求使用这种方法。有关说明,您可以阅读维基百科中的文章here。
“资金收集”的附加内容只是进一步的间接性,与要解决的问题无关。
基本任务是创建组或循环。
维基百科文章中描述的方法是使用 Two Line notation。因为在给定的问题中,只使用了索引,所以最终生活会更容易。我暂时忘记了这里的基于 1 的起始索引,将在后面的程序中简单地使用偏移量。
那么,给定的输入如何以 2 行符号显示?
1 2 3 4 5
2 3 1 4 5
而且,我们如何使用这种表示法找到组和循环。我们开始迭代第一行中的所有值。对于第一个索引1,我们存储数据,然后在1下面查找,找到一个2。这个2,我们在第一行再次搜索,存储它,然后在它下面查找。在那里我们找到了 3。存储 3 并查看它下面。我们找到一个 1。搜索 1 并尝试存储它,但是,它已经存在了。所以停止对第一行的第一个值的评估。结果组将是 1-2-3。
然后继续第一行中的下一个值。使用上述方法,我们将得到 2-3-1。对于第 1 行中的下一个值,我们将得到 3-1-2,然后只有 4 和 5。所以,循环,组是(1,2,3),(4),(5)。
非常简单。为了避免重复循环/组,我们将循环存储在 std::set
中。所以所有 cylce / group 成员都将是唯一的和排序的。这很重要,如果我们以后想优化算法。我们可以检查第一行的条目是否已经是循环/组的成员。那么这个评价就没有必要了。这可以为一大群志愿者节省精力。
另一方面,我们需要在这个集合中存储所有循环/组和搜索。因此,这也将花费空间和时间。我不确定,目前什么更好。但是我在下面显示的示例代码中留下了支票。
下一步优化。我们根本不需要 2 行。因为只存储索引。所以,第一行总是简单的索引 1...Number-of-volunteers。如果我们读入对值,那么我们将把对的第二个值存储在索引处,由对的第一个值表示。为了不错过其余的值,我们以 std::::vector
开头来初始化基本 std::iota
。
所以,我们初始化我们的 std::vector
。内容是1,3,4,5,那么我们读取1,2。意思是将第二个值 2 放在索引 1 处。内容现在是 2、2、3、4、5。然后阅读 2,3。意思是,把对 3 的第二个值放在位置 2。内容现在是 2,5。接下来是 3,1。所以把 1 放在位置 3。内容现在是 2,1,5。并且因为没有更多的输入,这就是最终的输入序列。
对于评估,上面已经描述过。我们现在遍历此 std::vector
中的所有元素。我们从索引 1 开始并将该值存储在循环/组中。在索引 1 处,我们找到 2。新索引将为 2。将其添加到循环/组(现在:1,2)。什么是索引 2?对,3。将其添加到循环/组(现在:1,3)。在索引 3 处,我们找到值 1。这已经在我们的循环/组中,因此我们停止对第一个索引的评估。第一个循环/组将是 (1,3)。
然后,我们去下一个索引,得到2,1。我们将值存储在 std::set
中,因此我们将再次获得 1,3。无需进一步评估。
依此类推。
找到循环/组后,我们根据循环/组中的索引计算集合的总和。并且,将此总和与当前最大值进行比较,如有必要,我们将对其进行更新。
基本上,我们不需要存储所有周期/组,但它可能会提高执行速度(虽然,我不确定)。
示例代码可能如下所示:
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <numeric>
#include <set>
#include <limits>
// Simulation of input data
std::istringstream inputData{R"(5
12 34 10 7 8
3
1 2
2 3
3 1)"};
// Functionality
int main() {
// All the following is the input handling -----------------------------------------------------------
size_t numberOfVolunteers{}; inputData >> numberOfVolunteers; // Read group size
std::vector<int>collections(numberOfVolunteers); // Get all "collection" values
std::copy_n(std::istream_iterator<int>(inputData),numberOfVolunteers,collections.begin());
size_t numberOfPairs{}; inputData >> numberOfPairs; // Number of pairs to read
std::vector<size_t> permutations(numberOfVolunteers); // Initialize our permutations vector
std::iota(permutations.begin(),permutations.end(),0);
for (size_t i{}; i < numberOfPairs; ++i) { // Get all cyclic permutations
size_t index{}; size_t target{}; inputData >> index >> target;
permutations[index - 1] = target - 1;
}
// Input done,now start algorithm -----------------------------------------------------------
std::vector<std::set<size_t>> allCycles{}; // All unique cycles / group of volunteers
int maxSum{ std::numeric_limits<int>::min() }; // And thsi will be the result of the program
// Go through all entries of the whole group
for (size_t currentIndex = 0U; (currentIndex < numberOfVolunteers); ++currentIndex) {
// If this menmber is already part of a cylce/group,then do not make an evaluation again
if (std::count_if(allCycles.begin(),allCycles.end(),[&](const std::set<size_t>& c) {return c.count(currentIndex); }) == 0) {
std::set<size_t> cycle{}; // Here we will store one cycle / group of volunteers
size_t index = currentIndex; // And we will follow the chain of successors starting with current index
// As long as we find successors
for (bool insertResult{ true }; insertResult; ) {
const auto& [newElement,insertOk] = cycle.insert(index); // Insert new group member
index = permutations[index]; // Set next successor
insertResult = insertOk; // Continue?
}
//Calculate the sum of the collections for this Cycle / Group
int sum{}; for (size_t index : cycle) sum += collections[index];
maxSum = std::max(sum,maxSum);
// Debug Output. Please uncomment,if you want to see debug output
std::cout << "Cycle: "; for (size_t index : cycle) std::cout << index + 1 << ' '; std::cout << " Sum: " << sum << " MaxSum : " << maxSum << '\n';
// Save current cycle
allCycles.emplace_back(std::move(cycle));
}
std::cout << maxSum << '\n'; // Show result
}
return 0;
}
如果内存消耗是一个问题,那么我会省略所有包含“allCycles”的引用和行
使用 Microsoft Visual Studio Community 2019 版本 16.8.2 进行编译和测试
使用 gcc 10.2 和 clang 11.0.1 额外编译和测试
语言 C++17
,您的第一种方法(通过 DFS 寻找群组)存在几个问题:
- 您在函数
v
中为visited
、adj
和solve()
声明(即分配的内存)。如果您的程序将为单个测试用例运行,这绝对没问题。从您的代码中,我假设您的代码需要多个测试用例。现在,每次调用solve()
方法时,都会为v
、visited
和adj
分配内存,并且当程序返回到main()
函数时,那些内存不会释放。- 最简单的解决方法是在全局范围内声明这些变量并在每个测试用例中重复使用它们。
- 在
dfs()
函数中,函数参数为:1 integer
、2 n-sized array
和1 map
,包含边信息。您使用call-by-value
传递这些参数,这意味着每次调用dfs()
函数时,这些参数都会在本地复制到该函数。- 解决方法是:(i) 要么使用
call-by-reference
,要么 (ii) 将这些参数保留在全局范围内,这样您就无需在每次调用 {{1} 时将它们作为参数传递} 函数。
- 解决方法是:(i) 要么使用
这里我已经解决了问题,看看:
dfs()
在您的第二种方法中(通过 union-find 查找组):
- 您的工会级别存在问题。根据{{3}}:
- 在高秩树的根下附加较小的秩树
- 如果等级相同,则以一为根,并将其等级加一
- 在找到 group-id 之前(通过检查
#include <iostream> #include <vector> #include <unordered_map> using namespace std; #define MAX_NODE 10005 unordered_map<int,vector<int>> adj; int values[MAX_NODE]; int visited[MAX_NODE]; int dfs(int v) { int ans = values[v]; visited[v] = 1; for (auto j : adj[v]) { if (visited[j] == 0) { ans = ans + dfs(j); } } return ans; } void solve() { int n; cin >> n; for (int i = 0; i < n; i++) { cin >> values[i]; visited[i] = 0; } int p; int ans = 0; cin >> p; while (p--) { int f,s; cin >> f >> s; f = f - 1; s = s - 1; adj[f].push_back(s); adj[s].push_back(f); } for (int i = 0; i < n; i++) { if (visited[i] == 0) { int val = dfs(i); if (val > ans) { ans = val; } } } cout << ans << endl; adj.clear(); } int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); // #ifndef ONLINE_JUDGE // freopen("input.txt","r",stdin); // freopen("output.txt","w",stdout); // #endif // ONLINE_JUDGE int t = 1; //cin >> t; while (t--) { solve(); } }
),进行额外的一轮压缩。如果联合查找树中有任何未压缩的路径,这将有助于解决问题。 - 不要在本地方法中声明繁重的数据结构(就像你使用的
parent
),它会杀死你的多个测试用例程序。我详细描述了对您的 DFS 解决方案分析的影响。
这里我已经解决了问题,看看:
map