【图论 有向图 环 传递闭包】P8186 [USACO22FEB] Redistributing Gifts S|普及+
本文涉及知识点
C++图论
P8186 [USACO22FEB] Redistributing Gifts S
题目描述
FJ 有 NNN 个礼物给他的 NNN 头奶牛,这 NNN 个礼物和 NNN 头奶牛都被标记为 1⋯N(1≤N≤500)1 \dotsm N (1 \le N \le 500)1⋯N(1≤N≤500) 。 每头奶牛都有一个愿望单,记录着一个含有 NNN 个礼物的排列。比起在愿望单中出现更晚的礼物,奶牛更喜欢先出现在愿望单中的礼物。
因为 FJ 太懒了,他直接把 iii 号礼物分配给了 iii 号奶牛。现在,奶牛们聚在了一起,决定重新分配礼物,以便在重新分配后,每头奶牛都能得到跟原来一样,或是它更喜欢的礼物。
对于每个 iii (iii 从 111 到 NNN),计算出重新分配后, iii 号奶牛可能拿到的最好的礼物(这个奶牛经过重新分配后能拿到的最喜欢的礼物)。
输入格式
第一行输入 NNN 。之后 NNN 行每行包含一个奶牛的愿望单。保证这 NNN 行都是 1⋯N1 \dotsm N1⋯N 的排列
输出格式
输出 NNN 行,第 iii 行输出重新分配后 iii 号奶牛可能得到的最好礼物。
样例解释
在这个例子中,有两种可能的分配方案
- 最初的方案:一号奶牛得到一号礼物,二号奶牛得到二号礼物,三号奶牛得到三号礼物,四号奶牛得到四号礼物
- 重新分配后的方案:一号奶牛得到一号礼物,二号奶牛得到三号礼物,三号奶牛得到二号礼物,四号奶牛得到四号礼物。可以发现一号和四号奶牛都拿不到比FJ分配的更好的礼物。不过二号和三号都可以。
输入输出样例 #1
输入 #1
4
1 2 3 4
1 3 2 4
1 2 3 4
1 2 3 4
输出 #1
1
3
2
4
说明/提示
- 2∼32 \sim 32∼3 号测试点满足 N≤8N \le 8N≤8
- 4∼114 \sim 114∼11 号测试点没有别的限制
由 tzyt 翻译
图论 有向图 环
如果奶牛i对礼物j的期望程度大于礼物i,则增加有向边i→ji\rightarrow ji→j。那们交换过程就是一个环。通过DFS标记所有的边是否在任意环上。DFS的规则如下:
一,每个连通区域都要DFS。
二,先DFS到的节点时祖先,后DFS到的节点是后代。
三,任意节点,只在第一次DFS是访问临接点。非第一次DFS时,将环上边i,j edge[i][j]=true。这个确保每条边只访问依次。
ispar[i] 记录节点i是否是当前节点的祖先,par依次记录当前节点的祖先。
edge[i][j] 为真 等效于 存在方案使得奶牛i能够得到礼物j。
时间复杂度:O(nn)
传递封包
令传递封包的矩阵为B,则 i能换到到礼物j ⟺\iff⟺ 存在i到j的边,b[j][i] = true。
时间复杂度:O(nnn)
有边0,无边1。传递封包转成多源最短路。
代码
核心代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include<array>#include <bitset>
using namespace std;template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {in >> pr.first >> pr.second;return in;
}template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t);return in;
}template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);return in;
}template<class T = int>
vector<T> Read() {int n;cin >> n;vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}
template<class T = int>
vector<T> ReadNotNum() {vector<T> ret;T tmp;while (cin >> tmp) {ret.emplace_back(tmp);if ('\n' == cin.get()) { break; }}return ret;
}template<class T = int>
vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}template<int N = 1'000'000>
class COutBuff
{
public:COutBuff() {m_p = puffer;}template<class T>void write(T x) {int num[28], sp = 0;if (x < 0)*m_p++ = '-', x = -x;if (!x)*m_p++ = 48;while (x)num[++sp] = x % 10, x /= 10;while (sp)*m_p++ = num[sp--] + 48;AuotToFile();}void writestr(const char* sz) {strcpy(m_p, sz);m_p += strlen(sz);AuotToFile();}inline void write(char ch){*m_p++ = ch;AuotToFile();}inline void ToFile() {fwrite(puffer, 1, m_p - puffer, stdout);m_p = puffer;}~COutBuff() {ToFile();}
private:inline void AuotToFile() {if (m_p - puffer > N - 100) {ToFile();}}char puffer[N], * m_p;
};template<int N = 1'000'000>
class CInBuff
{
public:inline CInBuff() {}inline CInBuff<N>& operator>>(char& ch) {FileToBuf();while (('\r' == *S) || ('\n' == *S) || (' ' == *S)) { S++; }//忽略空格和回车ch = *S++;return *this;}inline CInBuff<N>& operator>>(int& val) {FileToBuf();int x(0), f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行 return *this;}inline CInBuff& operator>>(long long& val) {FileToBuf();long long x(0); int f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行return *this;}template<class T1, class T2>inline CInBuff& operator>>(pair<T1, T2>& val) {*this >> val.first >> val.second;return *this;}template<class T1, class T2, class T3>inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val);return *this;}template<class T1, class T2, class T3, class T4>inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);return *this;}template<class T = int>inline CInBuff& operator>>(vector<T>& val) {int n;*this >> n;val.resize(n);for (int i = 0; i < n; i++) {*this >> val[i];}return *this;}template<class T = int>vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {*this >> ret[i];}return ret;}template<class T = int>vector<T> Read() {vector<T> ret;*this >> ret;return ret;}
private:inline void FileToBuf() {const int canRead = m_iWritePos - (S - buffer);if (canRead >= 100) { return; }if (m_bFinish) { return; }for (int i = 0; i < canRead; i++){buffer[i] = S[i];//memcpy出错 }m_iWritePos = canRead;buffer[m_iWritePos] = 0;S = buffer;int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);if (readCnt <= 0) { m_bFinish = true; return; }m_iWritePos += readCnt;buffer[m_iWritePos] = 0;S = buffer;}int m_iWritePos = 0; bool m_bFinish = false;char buffer[N + 10], * S = buffer;
};template<class T = int >
class CFloyd
{
public:CFloyd(int n, const T INF = 1000 * 1000 * 1000) :m_INF(INF){m_vMat.assign(n, vector<T>(n, m_INF));for (int i = 0; i < n; i++) {m_vMat[i][i] = 0;}}void SetEdge(int i1, int i2, const T& dis, bool bDirect = false){m_vMat[i1][i2] = min(m_vMat[i1][i2], dis);if (!bDirect) {m_vMat[i2][i1] = m_vMat[i1][i2];}}vector<vector<T>> Dis()const{auto vResMat = m_vMat;const int n = m_vMat.size();for (int i = 0; i < n; i++){//通过i中转for (int i1 = 0; i1 < n; i1++){if (m_INF == vResMat[i1][i]){continue;}for (int i2 = 0; i2 < n; i2++){//此时:m_vMat[i1][i2] 表示通过[0,i)中转的最短距离vResMat[i1][i2] = min(vResMat[i1][i2], vResMat[i1][i] + vResMat[i][i2]);//m_vMat[i1][i2] 表示通过[0,i]中转的最短距离}}}return vResMat;};vector<vector<T>> m_vMat;//结果串const T m_INF;
};class Solution {
public:vector<int> Ans(int N, vector<vector<int>>& mat) {vector<vector<int>> mat1(N, vector<int>(N, 1));//临接矩阵,有边0,没有变1。for (int i = 0; i < N; i++) {for (const auto& j : mat[i]) {if (j == i + 1) { break; }mat1[i][j - 1] = 0;}}CFloyd<> floyd(N);floyd.m_vMat = mat1;auto dis = floyd.Dis();vector<int> ans(N);iota(ans.begin(), ans.end(), 1);for (int i = 0; i < N; i++) {for (auto j : mat[i]) {j--;if (i == j) { break; }if ((0 == mat1[i][j]) && (0 == dis[j][i])) {ans[i] = j + 1;break;}}}return ans;}
};int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUG //ios::sync_with_stdio(0); cin.tie(nullptr);CInBuff<> in; COutBuff<10'000'000> ob;int N;in >> N;vector<vector<int>> mat(N);for (int i = 0; i < N; i++) {mat[i] = in.Read<int>(N);}
#ifdef _DEBUG printf("N=%d", N);Out(mat, "mat=");//Out(que, "que=");//Out(B, "B=");//Out(que, "que=");//Out(B, "B=");
#endif // DEBUG auto res = Solution().Ans(N, mat);for (const auto& i : res) {cout << i << '\n';}return 0;
}
单元测试
int N;vector<vector<int>> mat;TEST_METHOD(TestMethod1){N = 4,mat = { {1,2,3,4},{1,3,2,4},{1,2,3,4},{1,2,3,4} };auto res = Solution().Ans(N, mat);AssertV({ 1,3,2,4 }, res);}
扩展阅读
| 我想对大家说的话 |
|---|
| 工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
| 学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
| 有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
| 闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
| 子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
| 如果程序是一条龙,那算法就是他的是睛 |
| 失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。
