C. Book
题目传送门:
题面:
题目大意:
一本书有n个章节,每个章节都有可能有前置章节,对于某个章节i只有理解完他的全部前置章节才能理解章节i,每次看书从第一章看到最后一章,求最少要读几次能理解全部章节,没办法理解所有章节则输出-1。
思路A:
先写个dp。
对于某个章节a,如果他的前置章节b<a,由于读书次序从小到大,那么ab能同时理解,只要读1遍:
d
p
[
a
]
=
m
a
x
(
d
p
[
a
]
,
d
p
[
b
]
)
dp[a]=max(dp[a],dp[b])
dp[a]=max(dp[a],dp[b])
否则就要读2遍:
d
p
[
a
]
=
m
a
x
(
d
p
[
a
]
,
d
p
[
b
]
+
1
)
dp[a]=max(dp[a],dp[b]+1)
dp[a]=max(dp[a],dp[b]+1)
代码A:
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int N = 2e5 + 10;
int d[N], dp[N];
//每个点的入度,每章节的次数
vector<int> g[N];//存每个章节需要理解的各章节
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n;
for (int i = 1; i <= n; i++) {
g[i].clear();
dp[i] = 0;
d[i] = 0;
}
scanf("%d", &n);
int k, x;
for (int i = 1; i <= n; i++) {
scanf("%d", &k);
d[i] = k;
while (k--) {
scanf("%d", &x);
g[x].push_back(i);
}
}
queue<int> q;
for (int i = 1; i <= n; i++) {
if (!d[i])q.push(i), dp[i] = 1;
//先读第一遍
//已经读完的入队
}
while (q.size()) {
int t = q.front();
q.pop();
for (auto it:g[t]) {
//遍历以已读完的章节为前置章节的章节
d[it]--;
//读完一个前置了,门槛--;
if (t > it)
dp[it] = max(dp[it], dp[t] + 1);
else dp[it] = max(dp[it], dp[t]);
if (d[it] == 0)q.push(it);
}
}
int ans = 0;
int flag= 1;
for (int i = 1; i <= n; i++) {
ans = max(ans, dp[i]);
if (d[i])flag = 0;
}
if (flag == 0)cout << "-1" << endl;
//自环了
else cout << ans << endl;
}
return 0;
}
思路B
前向星存图
先健了,健完再写。