题意
现在你要给n个人分糖果,但是他们么个人都有一个嫉妒的人而且还有一个嫉妒值,比如现在有两个人a、b,那么人肯定不会自己嫉妒自己,那么他们两个会相互嫉妒,那么如果你先给a一个糖果那么b会嫉妒,反之b会嫉妒。那么你要确定一个给糖果的顺序保证所产生的嫉妒值之和最小。
思路
首先我们将n个人的嫉妒关系抽象成一个有向图,那么我们就会发现这个图至少会产生一个环,那么我们找出这个图中所有的环的有哪些点,我们只要找出每个环中最小的嫉妒值,再相加就是我们的答案。
由样例2我们就可以发现将会产生两个环,所以我们分别找出这两个环中最小的嫉妒值,第一个人和第五个人,那么我们一定可以找到一种排列只加上这两个人的嫉妒值,所以采用dfs跑图,值得注意的是当我们跑过的点记得打上标记以免使答案冗余。
因为我们找的只是环中的点,所以dfs只是跑这个图当我们发现一个点跑了两遍那么就已经产生了一个环,所以我们再跑一遍dfs以这个跑了两次的点为起点然后跑的过程中的这些点就是我们这个环中的点,记得打上标记。
代码
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr)
#define int long long
using namespace std;
const int N = 2e5 + 10;
int h[N], idx, n;
int x[N], c[N];
bool st[N], st2[N];
int st1[N];
map<int, int> mp;
struct node
{
int u, v;
}e[N];
void add(int u, int v)
{
e[++idx].v = v;
e[idx].u = h[u];
h[u] = idx;
}
vector<int> vc, ve;
int ans;
void dfs2(int u)//跑环
{
if (st2[u])
{
int res = ve.size();
int re = 1e9 + 10;
int cnt = 0;
for (int i = 0; i < ve.size(); i++)
re = min(re, c[ve[i]]);
ans += re;
return;
}
st2[u] = 1;
ve.push_back(u);
for (int i = h[u]; i; i = e[i].u)
{
int v = e[i].v;
dfs2(v);
}
ve.pop_back();
}
void dfs(int u, int cnt)//最开始的跑图
{
if (st[u])
{
dfs2(u);
return;
}
st[u] = 1;
mp[u] = cnt;
vc.push_back(u);
for (int i = h[u]; i; i = e[i].u)
{
int v = e[i].v;
if (mp.count(v))
{
if (mp[v] != cnt) return;//这个点已经被跑过了,不需要再跑了
}
mp[v] = cnt;
dfs(v, cnt);
}
vc.pop_back();
}
signed main()
{
IOS;
cin >> n;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
add(i, x);//链式前向星存图
}
for (int i = 1; i <= n; i++) cin >> c[i];
int cnt = 0;//标记
for (int i = 1; i <= n; i++)
{
if (!st[i]) cnt++, dfs(i, cnt);
}
cout << ans << endl;
return 0;
}