【学习】Codeforces Round 786 (Div. 3)G. Remove Directed Edges
题意:给出一个有向无环图,接下来你可以选择删除一组边,但是注意这组边删去后,要让所有的入度和出度都要比一开始小,同时要保证两个点之间能有一种方式走通即可,然后问剩下的联通点最多有多少。
思路:
1.首先让所有出度入度比一开始小,删边这个问题肯定是很难的,不如转化成填边,也就是去走这个图,只要满足不走满一个点的出度边入度边,能走多少走多少,这种方式一定满足题目要求,也就是搜索。
2.这道题最关键的点在于保证两个点要有一种方式走通。这把搜索限制成为了一条链。下面来解释
针对于某一点,我们找出它的最长链,最长链也就是指能有向边走出的最长路。
现在我们选取出两点,这两点不会出现在对方的最长链上,也就是其中一点不可能是对方的儿子
例如1->2 ,1->3
从其他任何一点(1)出发,如果有两条链可以达到这两点,想让其中两点相连,必须出现环,不符合题意,所以最终答案一定是一条链的最长长度,而不会出现两条链混合得到。
或者你可以看成拓扑排序
3.所以答案只要记忆化搜索,记录以每个点起始它能得到的最长链,然后最终选最大值就可以了
4.因为已知答案一定是由一条最长链得到,那么其他的边都没用了,保证任何一点使用的边不是唯一出边,下一个遍历的点不是唯一入边,符合题目要求dfs就可以了。
本人最大的问题就是思路没有明确,且代码写的不够仔细,导致逻辑上有bug出现问题,最终敲了好久算是自己做出来了(还看了样例,不然都意识不到)
代码:
#include <bits/stdc++.h>
#define int long long
#define int128 __int128
#define IOS \std::ios::sync_with_stdio(0); \std::cin.tie(0); \std::cout.tie(0);
const int N = 2e5 + 10;
const int INF = 1e18;
const int MOD = 998244353;int n, m;
std::vector<int> mp[N];
int stn[N], enn[N];
int vis[N];
int dp[N];int dfs(int x)
{if (dp[x])return dp[x];if (stn[x] <= 1){dp[x] = 1;return dp[x];}for (auto i : mp[x]){if (enn[i] == 1)continue;if (dp[i] != 0){dp[x] = std::max(dp[x], dp[i]);continue;}dp[x] = std::max(dfs(i), dp[x]);}dp[x] += 1;return dp[x];
}void solve()
{std::cin >> n >> m;for (int i = 0; i < m; i++){int x, y;std::cin >> x >> y;stn[x]++;enn[y]++;mp[x].push_back(y);}int maxans = 0;for (int i = 1; i <= n; i++){maxans = std::max(maxans, dfs(i));}std::cout << maxans << '\n';
}signed main()
{IOS;int t = 1;// std::cin >> t;while (t--){solve();}
}