题目大意:
n点n边有向图,可能包含自环和重边,现要求以最小花费选取一下点,使得无论从哪一个点出发都会经过所选的任意一个点。
思路:
我们考虑这样一种情况:
1---->2<-----3
^-----|
1、2号点形成一个环,3号点指向二号点。
case 1:如果在1、2号点选一个最小花费点x,那么所有点必将经过点x,满足题意。
case 2:如果选取了3号点,则必然不会比case 1的情况更优,因为总要在环内再选取一个点。
我们使用拓扑排序可以得到图中所有环,对于每一个环dfs找到最小花费的点。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PI;
const double eps = 1e-6;
const int N = 200010;
const int INF = 0x3f3f3f3f;
const int mod = 1000000007; //998244353
LL powmod(LL a, LL b) { LL res = 1; a %= mod; assert(b >= 0); for (; b; b >>= 1) { if (b & 1)res = res * a % mod; a = a * a % mod; }return res; }
int n, c[N], deg[N], to[N], ans;
bool vis[N];
void topo(int x) { //拓扑排序,一直删入度为0的点
vis[x] = 1;
deg[to[x]]--; //模拟删点,即边的另一端入度--
if (!deg[to[x]])
topo(to[x]);
}
int dfs(int x) {
vis[x] = 1;
if (!vis[to[x]]) //取最小花费
return min(dfs(to[x]), c[x]);
else
return c[x];
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) cin >> c[i];
for (int i = 1; i <= n; i++) {
int a; cin >> a;
to[i] = a;
deg[a]++;
}
for (int i = 1; i <= n; i++) {
if (!deg[i] && !vis[i]) { //从没访问过的入度为0点开始
topo(i);
}
}
//到这一步为止,任意不在环上的点x,vis[x]=1
for (int i = 1; i <= n; i++) {
if (!vis[i])
ans += dfs(i);
}
cout << ans << endl;
return 0;
}