将每个能被贿赂的人标记,若是有人不能被标记且他自己也不能被贿赂则说明无解。
若有解:对于一个环里面的所有点都可以互相到达,因此我们只需要利用tarjan缩点,缩点后将整个环的权值即为环中所有点的最小值,最后找到出度为0的点,将他们贿赂即可。
代码如下:
#include <stack> #include <cstdio> #include <cstring> #include <iostream> using namespace std; const int N = 3010, M = 8010, INF = 0x3f3f3f3f; int din[N]; int scc_cnt; int n, q, m; bool in_stk[N]; stack<int> stk; int id[N], sum[N], buy[N]; int h[N], e[M], ne[M], idx; int dfn[N], low[N], timestamp; void add(int a, int b)//邻接表 { e[idx] = b; ne[idx] = h[a]; h[a] = idx; idx ++ ; } void tarjan(int u)//tarjan缩点 { low[u] = dfn[u] = ++ timestamp; stk.push(u), in_stk[u] = true; for (int i = h[u]; i != -1; i = ne[i]) { int j = e[i]; if (!dfn[j]) { tarjan(j); low[u] = min(low[u], low[j]); } else if (in_stk[j]) low[u] = min(low[u], dfn[j]); } if (dfn[u] == low[u]) { int y; scc_cnt ++ ; do { y = stk.top(); stk.pop(); in_stk[y] = false; id[y] = scc_cnt; sum[scc_cnt] = min(sum[scc_cnt], buy[y]);//将环的权值记为环中每个点的最小值 } while (y != u); } } int main() { memset(h, -1, sizeof h);//初始化 memset(buy, 0x3f, sizeof buy); memset(sum, 0x3f, sizeof buy); cin >> n >> q; while (q -- ) { int index, cost; scanf("%d %d", &index, &cost); buy[index] = cost; } cin >> m; while (m -- ) { int a, b; scanf("%d %d", &a, &b); add(a, b); } for (int i = 1; i <= n; i ++ ) if (!dfn[i] && buy[i] != INF)//若i为被遍历,且i可以被贿赂 tarjan(i); for (int i = 1; i <= n; i ++ ) if (!dfn[i])//说明他不能被贿赂,无解 { puts("NO"); printf("%d\n", i); return 0; } for (int i = 1; i <= n; i ++ ) for (int j = h[i]; j != -1; j = ne[j]) { int k = e[j]; int a = id[i], b = id[k]; if (a != b) din[b] ++ ;//找到入度为0的点 } puts("YES"); int ans = 0; for (int i = 1; i <= scc_cnt; i ++ ) if (!din[i]) ans += sum[i];//将入度为0的点的权值相加 cout << ans << endl; return 0; }