{"title":"轮舞曲(round.cpp)","time_limit":"1s","space_limit":"512 MB","problem_description":"\n\n\n\n\nn人来参加舞会,编号为1~n,决定跳几段轮舞曲。\n\n舞会至少有2人,并且每个人正好有两个相邻的人。如果刚好有2个人参加舞会,\n那么他们左边和右边的相邻的人相同。\n\n每个人都恰好仅认识一个人,对于编号为i的人,他认识的人的编号为ai\n你需要决定安排多少支轮舞曲,满足参加舞会的每个参与者认识的那个人刚好与\n他相邻。\n\n例如,如果有6个人,他们认识的人的编号为 [2,1,4,3,6,5]\n则轮舞曲的最少数量为 1:\n·1−2−3−4−5−6−1\n最大为 3:\n·1−2−1\n·3−4−3\n·5−6−5\n\n","input_format":"第一行一个整数n,表示参与舞会的人数。\n第二行n个整数a1,a2,…,an,第i个数表示编号为i的人仅认识的那个人的编号为ai。\n保证输入的数据合法。","output_format":"一行两个整数,第一个为安排轮舞曲数量的最小值,第二个为安排轮舞曲数量的\n最大值。","input_example":"9\n2 3 2 5 6 5 8 9 8","output_example":"1 3","example_explanation":"最小数量的安排方式:\n·1-2-3-4-5-6-7-8-9\n最大数量的安排方式:\n·1-2-3\n·4-5-6\n·7-8-9\n","data_range_and_agreement":"对于40%的数据,1≤n≤6\n对于另外40%的数据,1≤n≤100,且满足a数组为长度为n的排列\n对于100%的数据,1≤n≤2×105,1≤ai≤n,且ai≠","content":"\n\n\n\n\n【算法思路】\n\n首先,我们可以将舞会的参与者和他们认识的人的关系表示为一个图,其中每个参与者是一个节点,边表示他们之间的认识关系。由题意可知,每个参与者恰好有两个相邻的人,因此图中的每个节点的度数都是2。\n\n我们可以通过遍历图的连通分量来确定安排轮舞曲的最小数量。对于每个连通分量,我们可以从其中的任意一个节点开始,沿着边依次遍历,直到回到起始节点。这样我们就能得到一个轮舞曲。如果连通分量的大小是k,那么最小数量的安排方式就是k / 2。\n\n对于最大数量的安排方式,我们可以将每个连通分量中的节点分成两组,每组是连通分量大小的一半。然后,我们可以将每组中的节点两两配对,每对节点之间跳一支轮舞曲。对于剩下的未配对的节点,我们可以将它们两两配对,每对节点之间跳一支轮舞曲。这样,我们就能得到最大数量的安排方式,即连通分量大小的一半。\n\n\n\n\n【算法步骤】\n\n1. 读入参与舞会的人数n和每个人认识的人的编号数组a。\n2. 初始化一个大小为n+1的visited数组,用于记录每个节点是否被访问过。\n3. 初始化最小数量minCount为0。\n4. 遍历每个节点i:\n - 如果节点i没有被访问过:\n - 将节点i标记为访问过。\n - 初始化当前连通分量的大小count为1。\n - 初始化当前节点cur为i。\n - 从当前节点cur开始,沿着边依次遍历,直到回到起始节点i:\n - 将当前节点cur标记为访问过。\n - 将当前节点cur的下一个节点设置为下一个要访问的节点next。\n - 将当前节点cur的下一条边设置为下一条要访问的边nextEdge。\n - 将下一个要访问的节点next和下一条要访问的边nextEdge标记为访问过。\n - 将当前节点cur更新为下一个要访问的节点next。\n - 将当前节点cur的下一条边设置为下一条要访问的边nextEdge。\n - 将当前节点cur的下一个节点设置为下一个要访问的节点next。\n - 将当前节点cur的下一条边设置为下一条要访问的边nextEdge。\n - 将当前节点cur的下一个节点设置为下一个要访问的节点next。\n - 将当前节点cur的下一条边设置为下一条要访问的边nextEdge。\n - 将当前节点cur的下一个节点设置为下一个要访问的节点next。\n - 更新当前连通分量的大小count加一。\n - 将当前连通分量的大小count除以2并加到最小数量minCount中。\n5. 初始化最大数量maxCount为0。\n6. 遍历每个节点i:\n - 如果节点i没有被访问过:\n - 将节点i标记为访问过。\n - 初始化当前连通分量的大小count为1。\n - 初始化当前节点cur为i。\n - 从当前节点cur开始,沿着边依次遍历,直到回到起始节点i:\n - 将当前节点cur标记为访问过。\n - 将当前节点cur的下一个节点设置为下一个要访问的节点next。\n - 将当前节点cur的下一条边设置为下一条要访问的边nextEdge。\n - 将下一个要访问的节点next和下一条要访问的边nextEdge标记为访问过。\n - 将当前节点cur更新为下一个要访问的节点next。\n - 将当前节点cur的下一条边设置为下一条要访问的边nextEdge。\n - 将当前节点cur的下一个节点设置为下一个要访问的节点next。\n - 将当前节点cur的下一条边设置为下一条要访问的边nextEdge。\n - 将当前节点cur的下一个节点设置为下一个要访问的节点next。\n - 将当前节点cur的下一条边设置为下一条要访问的边nextEdge。\n - 将当前节点cur的下一个节点设置为下一个要访问的节点next。\n - 更新当前连通分量的大小count加一。\n - 将当前连通分量的大小count除以2并加到最大数量maxCount中。\n7. 输出最小数量minCount和最大数量maxCount。\n\n\n\n\n【算法复杂度】\n\n遍历每个节点的时间复杂度为O(n),总共有n个节点,因此总时间复杂度为O(n)。\n\n\n\n\n【代码实现】\ncpp\n#include <iostream>\n#include <vector>\n\nusing namespace std;\n\nvoid dfs(vector<int>& a, vector<bool>& visited, int cur, int& count) {\n visited[cur] = true;\n int next = a[cur];\n int nextEdge = a[next];\n visited[next] = true;\n visited[nextEdge] = true;\n\n while (nextEdge != cur) {\n count++;\n cur = next;\n next = nextEdge;\n nextEdge = a[next];\n visited[next] = true;\n visited[nextEdge] = true;\n }\n}\n\nint main() {\n int n;\n cin >> n;\n vector<int> a(n + 1);\n for (int i = 1; i <= n; i++) {\n cin >> a[i];\n }\n\n vector<bool> visited(n + 1, false);\n int minCount = 0;\n\n for (int i = 1; i <= n; i++) {\n if (!visited[i]) {\n visited[i] = true;\n int count = 1;\n dfs(a, visited, i, count);\n minCount += count / 2;\n }\n }\n\n int maxCount = 0;\n visited.assign(n + 1, false);\n\n for (int i = 1; i <= n; i++) {\n if (!visited[i]) {\n visited[i] = true;\n int count = 1;\n dfs(a, visited, i, count);\n maxCount += count / 2;\n }\n }\n\n cout << minCount << " " << maxCount << endl;\n\n return 0;\n}\n\n\n


原文地址: https://www.cveoy.top/t/topic/qnMx 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录