当前位置: 首页 > news >正文

【缩点 拓扑序】P3119 [USACO15JAN] Grass Cownoisseur G|省选-

本文涉及知识点

C++图论

P3119 [USACO15JAN] Grass Cownoisseur G

题目描述

为了更好地管理牛群的放牧路线,Farmer John 在他的农场中安装了若干单向牛道。农场由 NNN 块草场组成,编号为 111NNN,每条单向牛道连接一对草场。例如,若存在一条从草场 XXXYYY 的路径,则牛可以从 XXX 前往 YYY,但不能从 YYY 返回 XXX

众所周知,Bessie 喜欢尽可能多地品尝不同草场的牧草。她每天从草场 111 出发,访问一系列草场后返回草场 111。她试图最大化沿途经过的不同草场数量(重复访问的草场只算一次)。

由于单向路径的限制,Bessie 担心这会减少她每日路线中可以访问的草场数量。她想知道如果她违反规则,在路线中最多逆向通过某条道路一次,最多能品尝多少草场的牧草。请计算她从草场 111 出发并返回的情况下,最多能访问的不同草场数量。注意 Bessie 在整个旅程中最多只能逆向通过一条道路,且同一条路径不能逆向两次。

输入格式

第一行包含两个整数 NNNMMM,表示草场数量和单向牛道数量(1≤N,M≤100,0001 \leq N, M \leq 100,0001N,M100,000)。

接下来 MMM 行每行描述一条单向牛道,包含两个不同的整数 XXXYYY,表示从 XXXYYY 的单向路径。保证每条路径不会重复出现。

输出格式

输出一行,表示 Bessie 在最多逆向通过一条道路的情况下,从草场 111 出发并返回时能访问的最大不同草场数量。

输入输出样例 #1

输入 #1

7 10 
1 2 
3 1 
2 5 
2 4 
3 7 
3 5 
3 6 
6 5 
7 2 
4 7

输出 #1

6

说明/提示

样例解析:

以下是样例输入的 ASCII 图示:

v---3-->6
7   | \ |
^\  v  \|
| \ 1   |
|   |   v
|   v   5
4<--2---^

Bessie 可以通过逆向路径 5→35\to 353 访问草场 1,2,4,7,2,5,3,11, 2, 4, 7, 2, 5, 3, 11,2,4,7,2,5,3,1。到达草场 333 后,若不再次逆向其他路径则无法前往 666

缩点 拓扑排序

注意:任意道路正向行驶的次数不限。逆行只能0次或1次。逆行和正向行驶互相不影响。
习惯从0开始。
性质一:最优解可能无逆行。如:0→1→00 \rightarrow 1 \rightarrow 0010
性质二:能够到达环上任意一点,则能够到达任意环。
小结一:环可以缩点。
dis1[i]记录从0出发到i经过的最多农场。
dis2[i]记录从i出发到达0经过的最多农场。可将边反向,和vis1[i]一样求。
情况一:不逆序,结果是dis1[0]。
情况二:逆序一次,我们假定逆行的边是uv。
前提一:存在路径一0→v0\rightarrow v0v
前提二:存在路径二u→0u \rightarrow 0u0
前提三:以上两条路径可以不包括u→vu \rightarrow vuv

前提三

性质三:如果0→v,包括u→v0 \rightarrow v,包括u \rightarrow v0v,包括uv,则:
存在0→u0 \rightarrow u0u的路径,即u在0所在的环。
性质五:如果u→0包括u→vu \rightarrow 0包括u \rightarrow vu0包括uv,则存在v→0v \rightarrow 0v0d路径。即v和0在同一个环。
枚举缩点后的边uv:
情况一;0≠u且0≠v0\neq u且0 \neq v0=u0=v,路径一和路径二,都不包括uv,正常处理。
情况二0==u且0==v0 == u且0 ==v0==u0==v,自环必须忽略,否则出错。
情况三0==u,0≠v0==u,0\neq v0==u,0=v ,路径一和路径二不逆序,先路径一,逆行(vu),路径二。下图的路径:
0→1→逆行00 \rightarrow 1_{\rightarrow}^{逆行} 001逆行0
在这里插入图片描述
情况四:0≠u,0==v0 \neq u,0 == v0=u,0==v。和情况三类似。

实现

ans = dis[0]。
ans = max缩点后存在边uv(ans,dis1[v]+dis2[u]−0点所在环大小)max_{缩点后存在边uv}(ans,dis1[v]+dis2[u] - 0点所在环大小)max缩点后存在边uv(ans,dis1[v]+dis2[u]0点所在环大小) 0所在的环,dis1和dis2各计算了一次。
除0外,dis1[v]和dis2[u]不包括相同的点,否则整个路径是环,会被缩点。

求dis1

初始dis1[0]=1,其它为0。按拓扑序逆序处理cur,通过child枚举cur缩点后的孩子。
dis1[child] = max(dis1[child],dis1[cur]+此点所在环大小)

经典样例

在这里插入图片描述

代码

核心代码

#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 <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 T1, class T2, class T3, class T4,class T5 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4,T5>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t) >> get<4>(t);return in;
}template<class T1, class T2, class T3, class T4, class T5,class T6 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4, T5,T6>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t) >> get<4>(t) >>get<5>(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();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;
};class CDGTopSort
{
public:template <class T = vector<int> >CDGTopSort(const vector<T>& vNeiBo) :m_vDeg(vNeiBo.size()), m_neiBo(vNeiBo) {const int N = vNeiBo.size();m_backNeiBo.resize(N);for (int cur = 0; cur < N; cur++){m_vDeg[cur] = vNeiBo[cur].size();for (const auto& next : vNeiBo[cur]){m_backNeiBo[next].emplace_back(cur);}}}void Init() {auto Add = [&](int i) {if (0 != m_vDeg[i]) { return; }m_que.emplace(i);};for (int i = 0; i < m_vDeg.size(); i++){Add(i);}while (m_que.size()){const int cur = m_que.front(); m_que.pop();if (!OnDo(cur)) { continue; }for (const auto& next : m_backNeiBo[cur]){m_vDeg[next]--;Add(next);}};}queue<int> m_que;vector<int> m_vDeg;vector<int> m_vSort;
protected:const vector<vector<int>>& m_neiBo;vector<vector<int>> m_backNeiBo;virtual bool OnDo(int cur) {m_vSort.emplace_back(cur);return true;};
};class CNeiBo
{
public:static vector<vector<int>> Two(int n, const vector<pair<int, int>>& edges, bool bDirect, int iBase = 0){vector<vector<int>>  vNeiBo(n);for (const auto& [i1, i2] : edges){vNeiBo[i1 - iBase].emplace_back(i2 - iBase);if (!bDirect){vNeiBo[i2 - iBase].emplace_back(i1 - iBase);}}return vNeiBo;}static vector<vector<int>> Two(int n, const vector<vector<int>>& edges, bool bDirect, int iBase = 0){vector<vector<int>>  vNeiBo(n);for (const auto& v : edges){vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase);if (!bDirect){vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase);}}return vNeiBo;}static vector<vector<std::pair<int, int>>> Three(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0){vector<vector<std::pair<int, int>>> vNeiBo(n);for (const auto& v : edges){vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase, v[2]);if (!bDirect){vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase, v[2]);}}return vNeiBo;}static vector<vector<std::pair<int, int>>> Three(int n, const vector<tuple<int, int, int>>& edges, bool bDirect, int iBase = 0){vector<vector<std::pair<int, int>>> vNeiBo(n);for (const auto& [u, v, w] : edges){vNeiBo[u - iBase].emplace_back(v - iBase, w);if (!bDirect){vNeiBo[v - iBase].emplace_back(u - iBase, w);}}return vNeiBo;}static vector<vector<int>> Mat(vector<vector<int>>& neiBoMat){vector<vector<int>> neiBo(neiBoMat.size());for (int i = 0; i < neiBoMat.size(); i++){for (int j = i + 1; j < neiBoMat.size(); j++){if (neiBoMat[i][j]){neiBo[i].emplace_back(j);neiBo[j].emplace_back(i);}}}return neiBo;}
};class CSCCTarjan {
public:CSCCTarjan(vector<vector<int>>& neiBo) :m_neiBo(neiBo) {const int N = neiBo.size();m_vTime.assign(N, -1);m_vBack.assign(N, -1);m_vIsStack.assign(N, false);for (int i = 0; i < N; i++) {DFS(i);}}void InitPtNew() {m_ptNew.resize(m_neiBo.size());iota(m_ptNew.begin(), m_ptNew.end(), 0);for (auto& v : m_sccs) {nth_element(v.begin(), v.begin(), v.end());m_v0.emplace_back(v[0]);for (int i = 1; i < v.size(); i++) {m_ptNew[v[i]] = v[0];}}}vector<vector<int>> GetNewNeiBo() {vector<vector<int>> neiBo(m_neiBo.size());for (int i = 0; i < neiBo.size(); i++) {const int n1 = m_ptNew[i];for (const auto& next : m_neiBo[i]) {const int n2 = m_ptNew[next];if (n1 == n2) { continue; }//自环neiBo[n1].emplace_back(n2);}}for (int i = 0; i < neiBo.size(); i++) {unordered_set<int> s(neiBo[i].begin(), neiBo[i].end());vector<int> tmp(s.begin(), s.end());neiBo[i].swap(tmp);}return neiBo;}vector<vector<int>> m_sccs;vector<int> m_v0, m_ptNew;
protected:void DFS(int cur) {if (-1 != m_vTime[cur]) { return; }m_vTime[cur] = m_vBack[cur] = m_iTimes++;m_vIsStack[cur] = true;m_sta.emplace(cur);for (const auto& next : m_neiBo[cur]) {if (-1 == m_vTime[next]) {DFS(next);m_vBack[cur] = min(m_vBack[cur], m_vBack[next]);}else if (m_vIsStack[next]) {m_vBack[cur] = min(m_vBack[cur], m_vTime[next]);}}if (m_vTime[cur] != m_vBack[cur]) { return; }vector<int> scc;while (m_sta.size()){auto u = m_sta.top(); m_sta.pop();scc.emplace_back(u);m_vIsStack[u] = false;if (cur == u) { break; }}m_sccs.emplace_back(scc);}vector<vector<int>>& m_neiBo;int  m_iTimes = 0;vector<int> m_vTime, m_vBack;vector<bool> m_vIsStack;stack<int> m_sta;
};
class Solution {
public:int Ans(const int N, vector<pair<int, int>>& edge) {for (auto& [u, v] : edge) { u--, v--; }auto neiBo = CNeiBo::Two(N, edge, true, 0);CSCCTarjan scc(neiBo);scc.InitPtNew();m_ringSize.assign(N, 0);for (int i = 0; i < N; i++) {m_ringSize[scc.m_ptNew[i]]++;}auto neiBo1 = scc.GetNewNeiBo();vector<vector<int>> neiBo2(N);for (int i = 0; i < N; i++) {for (const auto& j : neiBo1[i]) {neiBo2[j].emplace_back(i);}}assert(0 == scc.m_ptNew[0]);auto dis1 = More(N, neiBo1);auto dis2 = More(N, neiBo2);int ans = dis1[0];for (int u = 0; u < N; u++) {for (const auto& v : neiBo1[u]) {if ((0 == dis1[v]) || (0 == dis2[u])) { continue; }if ((0 == scc.m_ptNew[u]) && (0 == scc.m_ptNew[v])) { continue; }ans = max(ans, dis1[v] + dis2[u] - m_ringSize[0]);}}return ans;}vector<int> More(const int N, vector<vector<int>>& neiBo) {vector<int> ret(N);ret[0] = m_ringSize[0];CDGTopSort topSort(neiBo);topSort.Init();for (auto it = topSort.m_vSort.rbegin(); it != topSort.m_vSort.rend(); ++it) {if (0 == ret[*it]) { continue; }for (const auto& child : neiBo[*it]) {ret[child] = max(ret[child], ret[*it] + m_ringSize[child]);}}return ret;}vector<int> m_ringSize;
};
int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUGios::sync_with_stdio(0); cin.tie(nullptr); cout.tie(nullptr);	int N;cin >> N ;auto edge = Read<pair<int,int>>();
#ifdef _DEBUGprintf("N=%d",N);Out(edge, ",edge=");//Out(que, ",que=");
#endif // DEBUGauto res = Solution().Ans(N, edge);cout << res;return 0;
}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
员工说:技术至上,老板不信;投资人的代表说:技术至上,老板会信。
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步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++**实现。

http://www.dtcms.com/a/319090.html

相关文章:

  • 【关于Java中==和equals( )和hashCode( )三者异同】
  • 写Rust GPU内核驱动:GPU驱动工作原理简述
  • 【性能测试】---测试工具篇
  • 医疗人效管理新标杆:盖雅工场如何赋能健康服务企业提质增效
  • 「iOS」————自动释放池底层原理
  • CSS包含块与百分比取值机制完全指南
  • 数据分析——Pandas库
  • 添加内容溢出时显示完整内容提示的功能
  • QT5.15 mingw
  • c++之 栈浅析
  • Python 数据类型及数据类型转换
  • platform总线简介和使用场景说明
  • 基于Ruby的IP池系统构建分布式爬虫架构
  • 《算法导论》第 9 章 - 中位数和顺序统计量
  • 网页图片视频一键下载+视频去重修改 ,覆盖B站等多个平台
  • 【基础知识】springboot+vue 基础框架搭建(更新中)
  • 中国MCP市场:腾讯、阿里、百度的本土化实践
  • AI绘画:生成唐初李世民全身像提示词
  • 前后端加密传数据实现方案
  • 强反光干扰下读数误差↓79%!陌讯多模态算法在仪表盘识别场景的落地优化​
  • LINUX-文件查看技巧,重定向以及内容追加,man及echo的使用
  • 迅为RK3588开发板Android proc文件系统查询-内核版本查询
  • PyTorch RNN 名字分类器
  • 11-netty基础-手写rpc-支持多序列化协议-03
  • 【MySQL基础篇】:MySQL事务并发控制原理-MVCC机制解析
  • qt的元对象系统详解
  • 2深度学习Pytorch-神经网络--全连接神经网络、数据准备(构建数据类Dataset、TensorDataset 和数据加载器DataLoader)
  • Activiti 中各种 startProcessInstance 接口之间的区别
  • [激光原理与应用-169]:测量仪器 - 皮秒激光器研发过程中所需要的测量仪器
  • 2025年机械工程与自动化技术国际会议(ICMEAT 2025)