Acwing.379 捉迷藏(最小路径重复点覆盖)

传送门

1.前置知识

1.最小路径点覆盖

DAG中选出最小数量的不相交路径(无公共点),将所有点覆盖。
求法:将DAG中的点拆成出点和入点,构成一个二分图。
则原图的最小路径点覆盖转化到新图中:
1.无公共点->每个点最多只有一个出度一个入度 ->…->新图中的任意两条边之间不相交-> 新图中的边都是匹配边。

小结论:每个路径终点 对应 一个左侧非匹配点
<=> 让左侧非匹配点最少 n-m
<=> 让左侧匹配点最多 m
<=> 找最大匹配边数 m
->二分图最大匹配数 = 总点数-最小路径点覆盖

2.最小路径重复点覆盖

相较于最小路径点覆盖,取消了无公共点的限制
求法:先做一次传递闭包。
原图G最小路径重复点覆盖==新图G’最小路径点覆盖。
(证明没怎么看懂 ,wsfw)

2.题意分析

求DAG中 选择尽可能多的点,使两点之间不能相互到达。
等价于求最小路径重复点覆盖。
最小路径重复点覆盖中的每一条路径,都只能选择一个点。
最优解也就是每条路径都要选一个点。

3.代码

#include <bits/stdc++.h>

using namespace std;
//-----pre_def----
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
#define fir(i, a, b) for (int i = (a); i <= (b); i++)
#define rif(i, a, b) for (int i = (a); i >= (b); i--)
#define endl '\n'
#define init_h memset(h, -1, sizeof h), idx = 0;
#define lowbit(x) x &(-x)

//---------------
const int N = 200 + 10, M = 30010;
int n, m;
bool st[N], d[N][N];
int match[N];

bool find(int u) //匈牙利
{
    for (int i = 1; i <= n; i++)
    {
        if (d[i][u] && !st[i])
        {
            st[i] = true;
            int t = match[i];
            if (t == 0 || find(t))
            {
                match[i] = u;
                return true;
            }
        }
    }
    return false;
}

void init() {}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    int StartTime = clock();
#endif
    scanf("%d%d", &n, &m);
    while (m--)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        d[a][b] = true;
    }
    //传递闭包
    fir(k, 1, n)
        fir(i, 1, n)
            fir(j, 1, n)
                d[i][j] |= d[i][k] & d[k][j];

    //求最大匹配
    int res = 0;
    fir(i, 1, n)
    {
        memset(st, 0, sizeof st);
        res += find(i);
    }
    printf("%d\n", n - res);
#ifndef ONLINE_JUDGE
    printf("Run_Time = %d ms\n", clock() - StartTime);
#endif
    return 0;
}

再战图论

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值