Problem G
dag上的dp
题意
给你一个有向无环图,你需要删去其中的若干条边,使得
- 对于每个节点,它的入度比原来小,或原来即为0
- 对于每个节点,它的出度比原来小,或原来即为0
在删边之后的图中取出一个点集 S S S, 满足 S S S 中任意两点 u , v u,v u,v 或 u u u 可达 v v v 或 v v v 可达 u u u,问 S S S 中元素最多有多少个?
思路
首先满足条件的点集
S
S
S ,一定是一条链。所以我们最终的
S
S
S 只需要保留一条链,其它的边都没有必要。
问题类似于在一个dag上找最长链,于是考虑dp。设
d
p
i
dp_i
dpi 表示从
i
i
i 出发的链最多能连的点的数量。
i
n
i
,
o
u
t
i
in_i,out_i
ini,outi 分别表示
i
i
i 的入度和出度。
如果
o
u
t
i
<
2
out_i<2
outi<2,那么它的这条出边一定会被删掉,所以
d
p
i
=
1
dp_i=1
dpi=1 。若
o
u
t
i
≥
2
out_i\geq 2
outi≥2 ,且满足出边所连的点
j
j
j 的
i
n
j
≥
2
in_j \geq 2
inj≥2 ,那么这条
i
t
o
j
i \ to\ j
i to j 的边就可以保留,
d
p
i
=
d
p
j
+
1
dp_i=dp_j+1
dpi=dpj+1,否则如果
i
n
j
=
1
in_j=1
inj=1,则由条件1,这条边也需要删掉,也就是
i
,
j
i,j
i,j 无法相连。
代码
int n, m;
int in[maxn], out[maxn];
vector<int> e[maxn];
int dp[maxn];
int ans;
void dfs(int cur) {
if(dp[cur]) return;
dp[cur] = 1;
if(out[cur] < 2) {
return;
}
for(auto to : e[cur]) {
dfs(to);
if(in[to] > 1)
chmax(dp[cur], dp[to] + 1);
}
chmax(ans, dp[cur]);
}
void solve() {
cin >> n >> m;
ans = 1;
for(int i = 1; i <= n; i++) dp[i] = 0;
for(int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
in[v]++;
out[u]++;
e[u].pb(v);
}
for(int i = 1; i <= n; i++) {
dfs(i);
}
cout << ans << endl;
}