题意:
给定一个有向图,可能会有重边和自环。问是否会有一条路径使得图中每个点都至少被访问一次。 n≤50
解释:
其实这题并不难,因为数据范围较小,所以可以有很多方法暴力解决!
看了一下TC的官方题解,感觉学到了一波姿势!第一点 : 首先肯定是要缩点,一般是Tarjan咯!
题解是提供了一种 N3 的缩点的方法:
用邻接矩阵表示这个图,然后 Floyd 处理出任意两个点是否可达。
然后就可以达到缩点的目的了!第二点:缩点后得到一个 DAG,然后判断是否可以一条路径覆盖所有的点,这个可以用 最小路径覆盖解决,也就是经典的二分图匹配。
题解给出的是 topsort :
按拓扑排序的方法去选出路径上的每个点,最后判断一下选出的点的序列中相邻的两个点之间是否存在可达就行了!思考:这种方法确实可以去解决这种是否存在一条路径覆盖所有点的问题,而且效率是高于二分图匹配的,但是这种方法是否可以代替用二分图匹配去解决最小路径覆盖的问题呢?
也就是说,我们先按拓扑排序的方法得到选出的点的序列,然后判断这个序列中相邻的点 u,v ,是否满足 reach[u][v]==1 ,也就是是否可达,如果不满足就 ans++ ,这样得到最终的最小路径覆盖!这样的做法是否正确呢?
代码:
string check(vector<int> z0, vector<int> z1)
{
// create the graph from input, adjacency matrix:
int n = z0.size();
vector< vector<int> > a(n, vector<int>(n,0) );
for (int i = 0; i < n; i++) {
a[i][z0[i]] = 1;
a[i][z1[i]] = 1;
}
// Floyd-Warshall to quickly get which vertices are reachable from which starting points:
auto d = a;
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
d[i][j] = (d[i][j] || (d[i][k] && d[k][j]) );
}
}
}
// if a vertex u is reachable from vertex v and vice versa, then they
// belong to the same SCC, use that information to build a SCC graph:
vector<int> node_compo(n, -1);
int t = 0;
for (int i = 0; i < n; i++) {
if (node_compo[i] == -1) {
node_compo[i] = t++;
for (int j = 0; j < n; j++) {
if ( d[i][j] && d[j][i] ) {
node_compo[j] = node_compo[i];
}
}
}
}
vector< set<int> > g(t) , rg(t);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
int u = node_compo[i];
int v = node_compo[j];
if ( (u != v) && a[i][j] ) {
g[u].insert(v);
rg[v].insert(u);
}
}
}
// topsort the SCC graph:
vector<int> top_sort(t);
vector<bool> added(t, false);
for (int i = 0; i < t; i++) {
int pick = -1;
for (int j = 0; j < t; j++) {
if ( ! added[j] ) {
int cnt = 0;
for (int k : rg[j]) {
if ( !added[k] ) {
cnt++;
}
}
if (cnt == 0) {
pick = j;
}
}
}
if ( pick == -1) {
return "Does not exist";
} else {
top_sort[i] = pick;
added[pick] = true;
cout << "[";
for (int i = 0; i < n; i++) {
if (node_compo[i] == pick) {
cout << i << " ";
}
}
cout << "]" << endl;
}
}
if (top_sort[0] != 0) {
return "Does not exist";
}
// check the path:
for (int i = 1; i < t; i++) {
if ( g[ top_sort[i-1] ].count( top_sort[i]) == 0) {
return "Does not exist";
}
}
return "Exists";
}