题目:http://poj.org/problem?id=1422
题意:
n*n的方阵,存在若干条有向边相连,选择若干个点组成的路为一条参观路径,所有的路径都不存在相同点,选择最少路径,使得这些路径包括所有节点,即最小路径覆盖问题
分析:
在有向无环图中,假设没有边相连,则所有节点都是一条长度为0的路劲,此时最小路径覆盖数为总结点数
若此时多出一条边1-2,则两个节点1、2为同一条路劲,此时最小路径覆盖数-1
若再多一条1-3,则最小路径覆盖数不变,因为起点与上一次相同,即两条边不是匹配边,只能选1-2或1-3
若多的第二条为2-3,则与第一条路1-2为同一条路劲,此时最小路径覆盖数-1
......
综上,
最小路劲覆盖数 == 总节点数 - 最大匹配数
代码:
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <string>
#include <math.h>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <map>
using namespace std;
#define MAX 200
#define MIN -1e5
#define INF 0x7f7f7f7f
int t, n, m;
int x[MAX], y[MAX]; // 行、列点集合
int Edge[MAX][MAX]; // 有向无环图
int vis[MAX]; // path递归防止死循环的访问数组
int path(int u) // 设置 或 更新最大匹配的边
{
for(int v = 1; v<=n; v++) // 便利所有y(列)集合 节点
{
if(vis[v] == 0 && Edge[u][v] == 1) // 递归中未访问 && 该列节点v与行节点u有变相连(有方向 u->v)
{
vis[v] = 1;
if(y[v] == -1 || path(y[v])) // 该列节点v没有被行节点u匹配过 || 已经被行节点y【v】匹配过,尝试是否能够更新该匹配
{
x[u] = v;
y[v] = u;
return 1;
}
}
}
return 0;
}
void solve()
{
int ans = 0;
memset(x, -1, sizeof(x));
memset(y, -1, sizeof(y));
for(int u = 1; u<=n; u++) // 遍历所有x行节点
{
if(x[u] == -1) // 未被匹配过
{
memset(vis, 0, sizeof(vis));
ans += path(u); // 匹配成功, 最大匹配数+1
}
}
printf("%d\n", n - ans); // 最小路径覆盖 == 总节点数 - 最大匹配数
}
int main()
{
int i, j;
//freopen("a.txt", "r", stdin);
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &m);
int u, v;
memset(Edge, 0, sizeof(Edge));
for(i = 0; i<m; i++)
{
scanf("%d%d", &u, &v);
Edge[u][v] = 1;
}
solve();
}
return 0;
}