大意不再赘述。
思路:
第1问:求一个有向图的最小点基。
第2问:求连接最小的边使得有向图变成一个强连通图。
最小点基怎么求?首先要去找最高强连通分量,即入度为0的强连通分量。最小点基就是从最高强连通分量中任选一个顶点,组成的顶点集B就是图G的一个最小点基。
连接最小的边使得有向图变为一个强连通图,即找入度为0与出度为0的最大值即可。
什么是最小权点基呢?设图G的每个顶点Vi都有一个非负的权值ai,使得顶点对应的权值ai之和最小的点基称为最小权点基。求最小权点基的算法是从最高强连通分量里取权值最小的顶点,组成最小权点基。
总结一下:最小点基是从最高强连通分量里任选一个顶点,组成最小点基。而最小权点基,是从最高强连通分量里取权值最小的顶点,组成最小权点基。
具体的题目有:POJ 1236(最小点基)、POJ 3592(最小权点基)
#include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <string> using namespace std; const int MAXN = 1010; const int MAXM = 10010; const int INF = 0x3f3f3f3f; struct Edge { int v, next; }edge[MAXM]; int n, m; int cnt; int scnt, top, tot; int first[MAXN], dfn[MAXN], low[MAXN], ins[MAXN], stack[MAXN]; int belong[MAXN]; int ind[MAXN], outd[MAXN]; void init() { cnt = 0; scnt = tot = top = 0; memset(first, -1, sizeof(first)); memset(ind, 0, sizeof(ind)); memset(outd, 0, sizeof(outd)); memset(ins, 0, sizeof(ins)); } void read_graph(int u, int v) { edge[cnt].v = v; edge[cnt].next = first[u], first[u] = cnt++; } void dfs(int u) { int v; low[u] = dfn[u] = ++tot; ins[u] = 1; stack[top++] = u; for(int e = first[u]; e != -1; e = edge[e].next) { v = edge[e].v; if(!dfn[v]) { dfs(v); low[u] = min(low[u], low[v]); } else if(ins[v]) { low[u] = min(low[u], dfn[v]); } } if(low[u] == dfn[u]) { scnt++; do { v = stack[--top]; belong[v] = scnt; ins[v] = 0; }while(u != v); } } void Tarjan() { for(int i = 1; i <= n; i++) if(!dfn[i]) dfs(i); } void read_case() { init(); for(int u = 1; u <= n; u++) { int v; while(scanf("%d", &v) && v) { read_graph(u, v); } } } void solve() { read_case(); Tarjan(); for(int u = 1; u <= n; u++) { for(int e = first[u]; e != -1; e = edge[e].next) { int v = edge[e].v; if(belong[u] != belong[v]) { outd[belong[u]]++; ind[belong[v]]++; } } } int ans1 = 0, ans2 = 0; for(int i = 1; i <= scnt; i++) { if(!ind[i]) ans1++; if(!outd[i]) ans2++; } printf("%d\n", ans1); printf(scnt == 1? "0\n":"%d\n", max(ans1, ans2)); } int main() { while(~scanf("%d", &n)) { solve(); } return 0; }