prologue
看见题解区好多神犇都是用 网络流 来做的,但是蒟蒻在刚学完 二部图 之后就来刷题了,对于这个题的路径输出有一个 比较新颖 的搞法,所以说就来写了这篇题解。
analysis
首先,我们为了将它转换成为一个 二部图,我们需要对它进行拆点操作(其实最后我跑起来并没有拆点),然后对它进行分析。
(下面左部为出度的点,右部为入度的点)
这个时候,原图中的每条路径转化到新图中,每一个点都可以对应出来一个匹配。每一条路径的 终点 都会对应到 左部 一个 非匹配点。这个时候我们要求 最小路径覆盖,就等价于 左部非匹配点最少,即这个图的 最大匹配。我们求最大匹配可以使用 匈牙利算法(之后输出合法路径需要)。
记我们的 最大匹配 为 \(m\),最小路径覆盖 就为 \(n - m\)。
之后我们再考虑怎么去输出合法路径。
我们观察我们上面匈牙利算法中的 \(match\) 数组,每一个 右部 点的 \(match\) 都会对应到我们一个新图中的一个 左部点,我们再将这个点放到 右部点 来看,会由它的 \(match\) 对应一个 左部点。重复以上过程,直到我们无法再匹配 左部点。
这个不断往回匹配的过程我们可以用 递归 来解决,边界条件就是匹配到 \(0\)。我们只需要在跑这个递归的过程中用一个 \(vector\) 来记录路径就行。
code time
(我下面的这个 \(cnt\) 写的稀碎,我知道为啥,但是调不出来,除了占空间之外好像没啥缺点,还能当作一个(虚假的)可持久化来看)
using namespace std;
#define ll long long
#define rl register ll
#define x first
#define y second
typedef pair<ll, ll> pll;
const ll N = 160;
ll n, m;
bool st[N], g[N][N];
ll cnt, pre_cnt;
ll match[N];
pll pre[N];
vector<ll> path[N];
inline bool find(ll u)
{
for(rl i=1; i <= n; ++ i) if(g[u][i] && !st[i])
{
st[i] = true;
ll t = match[i];
if(!t || find(t))
{
match[i] = u;
return true;
}
}
return false;
}
inline void get_path(ll u)
{
path[cnt].push_back(u);
st[u] = 1;
if(!pre[u].y) { cnt ++ ; return ;}
get_path(pre[u].y);
}
inline bool cmp(pll a, pll b)
{
return a.y > b.y;
}
int main()
{
cin >> n >> m;
for(rl i=1; i <= m; ++ i)
{
ll a, b;
cin >> a >> b;
g[a][b] = true;
}
ll res = 0;
for(rl i=1; i <= n; ++ i)
{
memset(st, 0, sizeof st);
if(find(i)) res ++ ;
}
memset(st, 0, sizeof st);
for(rl i=1; i <= n; ++ i)
pre[i] = {i, match[i]};
for(rl i=n; i; -- i) if(!st[i] && pre[i].x) get_path(pre[i].x);
for(rl i=cnt - n + res; i < cnt; ++ i)
{
for(rl j : path[i])
cout << j << " ";
cout << endl;
}
cout << n - res << endl;
return 0;
}
标签:匹配,ll,路径,最小,match,rl,P2764,define
From: https://www.cnblogs.com/carp-oier/p/17731169.html