题意:给出一个T,表示之后有T组样例,随后给出一个N和M,表示编号为1~N的点,随后M行,每行一个u和v表示一条有向边u->v,求这个图的最小路径覆盖(路径不可相交).
题解:首先我们需要对这个图跑Tarjan的强连通缩点得到一个有向无环图,再对新的图求最小路径覆盖即可.
(最小路径覆盖=图的点数-最大匹配数)
注意:该题求的是路径不可相交的最小路径覆盖,若是路径可相交做法如下:
先使用Floyd获得该图各点之间的可达性,忽略其中间过程,然后在跑最大匹配即可,即多了一个Floyd的预处理过程.
代码如下:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<string>
#include<cstring>
#include<map>
#include<vector>
using namespace std;
#define ll long long
const int maxn = 20005;
struct Hungary //最大匹配
{
vector<int> g[maxn]; //关系图
int from[maxn], mx[maxn];
bool use[maxn];
int n, ans;
void init(int nn) //初始化
{
n = nn;
for(int i=1;i<=n;i++)
g[i].clear();
}
void add_edge(int u, int v) //填加有向边
{
g[u].push_back(v);
}
bool match(int x) //匹配
{
for(int i=0;i<g[x].size();i++)
if (!use[g[x][i]])
{
use[g[x][i]] = true;
if (from[g[x][i]] == -1 || match(from[g[x][i]]))
{
from[g[x][i]] = x;
mx[x] = g[x][i];
return true;
}
}
return false;
}
int get_ans() //求得最大匹配值
{
ans = 0;
memset(from, -1, sizeof(from));
memset(mx, 0, sizeof(mx));
for(int i=1;i<=n;i++)
{
memset(use, 0, sizeof(use));
if (match(i))ans++;
}
return ans;
}
}H;
struct Tarjan {
int n; //点的个数
vector<int>e[maxn]; //邻接表存图
int DFN[maxn], LOW[maxn];
int index; //编辑计数器
int stk[maxn]; //栈
bool ins[maxn]; //记录点是否在栈中
int top;
vector<vector<int> >ans;
void init(int N) { //初始化
n = N;
ans.clear();
for (int i = 1; i <= n; i++)
e[i].clear();
top = 0;
index = 0;
memset(DFN, -1, sizeof(DFN));
memset(ins, 0, sizeof(ins));
}
void add_edge(int u, int v) { //添加边
e[u].push_back(v);
}
void dfs(int u) { //从u点开始搜索
DFN[u] = LOW[u] = ++index;
stk[top++] = u; //为了记录这个连通分量中的节点
ins[u] = true;
for (int i = 0; i < e[u].size(); i++) {
int v = e[u][i];
if (DFN[v] == -1) {
dfs(v);
LOW[u] = min(LOW[u], LOW[v]);
}
else if (ins[v])
LOW[u] = min(LOW[u], DFN[v]);
}
if (DFN[u] == LOW[u]) {
vector<int>q;
int v = stk[top - 1];
while (u != v) {
q.push_back(v);
ins[v] = false;
top--;
v = stk[top - 1];
}
q.push_back(u);
ins[u] = false;
top--;
ans.push_back(q);
}
}
void solve() { //运行该函数后产生答案
for (int i = 1; i <= n; i++)
if (DFN[i] == -1) dfs(i);
}
}T;
int num[maxn];
bool vis[maxn];
int n, m;
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
int u, v;
T.init(n); //Tarjan初始化
while (m--) { //建有向图
scanf("%d%d", &u, &v);
T.add_edge(u, v);
}
T.solve(); //获得该有向图的所有强连通分量
for (int i = 0; i < T.ans.size(); i++) //缩点
for (int j = 0; j < T.ans[i].size(); j++)
num[T.ans[i][j]] = i + 1;
H.init(T.ans.size()); //Hungary初始化
for (int i = 1; i <= n; i++) //建新有向无环图
for (int j = 0; j < T.e[i].size(); j++) {
if (num[i] == num[T.e[i][j]]) continue;
H.g[num[i]].push_back(num[T.e[i][j]]);
}
//以上为Tarjan算法的强连通缩点操作
printf("%d\n", T.ans.size() - H.get_ans());//最小路径覆盖=点数-最大匹配数
}
return 0;
}