思路
- 如果某个罪犯不能贿赂或者没人揭发他,输出最小编号即可,怎么输出最小编号?我们可以在进行tarjan算法时,判断时间戳为0的结点的同时进行判断是否能被贿赂,如果不能则不进行tarjan。从1到n枚举看是否有时间戳为0的结点就是最小的编号。
2.存在解分两种情况:第一,起始结点不形成环,那么就是入度为0的结点贿赂总值;第二,起始结点形成环,那么就是求这个环中最小贿赂值。结合这两点,进行缩点后求入度为0的结点贿赂总值。
AC代码
#include<cstdio>
#include<iostream>
#include<stack>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
#define IOS ios::sync_with_stdio(false)
const int maxn = 1e4;
const int inf = 0x3f3f3f3f;
int dfn[maxn], low[maxn], color[maxn], inde[maxn], cost[maxn], sum[maxn], tot, ind = 1;
bool instack[maxn];
stack<int> st;
vector<int> G[maxn];
void tarjan(int u){
dfn[u] = low[u] = ind++;
st.push(u);
instack[u] = true;
for (int v : G[u]){
if (!dfn[v]){
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (instack[v]){
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]){
tot++;
int now;
do{
now = st.top(); st.pop();
instack[now] = false;
color[now] = tot;
sum[tot] = min(sum[tot], cost[now]);
} while(now != u);
}
}
void solve(){
int n, p, r;
scanf("%d%d", &n, &p);
memset(cost, inf, sizeof(cost));
memset(sum, inf, sizeof(sum));
while (p--){
int i, w;
scanf("%d%d", &i, &w);
cost[i] = w;
}
scanf("%d", &r);
for (int i = 1; i <= r; ++i){
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
}
for (int i = 1;i <= n; ++i){
if (!dfn[i] && cost[i] != inf){
tarjan(i);
}
}
for (int i = 1; i <= n; ++i){
if (!dfn[i]){
printf("NO\n%d\n", i);
return;
}
}
for (int u = 1; u <= n; ++u){
for (int v : G[u]){
if (color[u] != color[v]){
inde[color[v]]++;
}
}
}
int ans = 0;
for (int i = 1; i <= tot; ++i){
if (!inde[i]){
ans += sum[i];
}
}
printf("YES\n%d\n", ans);
}
int main(){
solve();
return 0;
}