我是靠谱客的博主 瘦瘦大神,最近开发中收集的这篇文章主要介绍G - Outing(tarjan缩点+依赖背包),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

这是个的依赖背包问题。我们由一个点 i 向他得依赖点 a[i]建边,我们会建成一个可能存在环的图,我们不难发现,对于图中的每一个联通分量来说最多只有一个环,而且在缩点后,这个环必然是这棵树的祖先结点。什么只有一个环?,根据本题题意,每一个点最多有一个依赖点,所以每一个点的出度为1,如果联通分量中出现了环,那么环中每一个点的出边必然指向环内的一个点,不会指向环外的点,所以这个环在缩点后出度必然为0。如果该连通分量中存在两个环,那么这两个环因为都没有出度,所以不可能出现由一个环出发走到另一个环的可能,因为所有结点的出度为1,该联通分量的的任意一个结点都有只有一个路径可走,假设路径A能走到第一个环,B能走到第二个环,路径A和路径B之间必然不连通,所以这两个环必然位于两个联通分量之中。

tarjan缩点之后,我们根据缩点后的图用并查集来合并同一联通分量的点,并且统计这个联通分量中祖先结点的大小(如果有环的话这个祖先结点的大小就是环的大小)。此时我们边可以将这题转化为分组背包问题,每一个联通分量便是分成了一组,每一组内的祖先结点大小即为该组中最小的选择数量,最大数量为整个联通分量的大小。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <sstream>
#include <cmath>
#include <queue>
#include <bitset>
#include <vector>
#include <map>
#define int long long
#define endl 'n'
#define lowbit(x) x &(-x)
#define mh(x) memset(x, -1, sizeof h)
#define debug(x) cerr << #x << "=" << x << endl;
#define brk exit(0);
using namespace std;
const int N = 1e6 + 10;
const int M = 2 * N;
const int mod = 998244353;
const double esp = 1e-6;
const double pi = acos(-1);
typedef pair<int, int> PII;
typedef long long ll;
int a[N];
vector<int> g[N];
int dfn[N], low[N], id[N], Size[N], timestamp, scc_cnt;
int stk[N], top;
bool in_stk[N];
int f[N];
int p[N];
map<int, pair<int, int>> mp;
void tarjan(int u)
{
    dfn[u] = low[u] = ++timestamp;
    stk[++top] = u;
    in_stk[u] = true;
    for (auto j : g[u])
    {
        if (!dfn[j])
        {
            tarjan(j);
            low[u] = min(low[u], low[j]);
        }
        else if (in_stk[j])
        {
            low[u] = min(low[u], low[j]);
        }
    }
    if (dfn[u] == low[u])
    {
        int y;
        ++scc_cnt;
        do
        {
            y = stk[top--];
            in_stk[y] = false;
            id[y] = scc_cnt;
            Size[scc_cnt]++;
        } while (y != u);
    }
}
int find(int x)
{
    if (x != p[x])
        p[x] = find(p[x]);
    return p[x];
}
signed main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        g[i].push_back(a[i]);//建图
    }
    for (int i = 1; i <= n; i++)
    {
        if (!dfn[i])
            tarjan(i);
    }
    for (int i = 1; i <= n; i++)
        p[i] = i;
    for (int i = 1; i <= n; i++)//并查集合并连通分量
    {
        int x = id[i], y = id[a[i]];
        if (find(x) !=find(y))
        {
            p[find(x)] = p[find(y)];
        }
    }
    for (int i = 1; i <= scc_cnt; i++)//统计该组的大小和最小选择人数
    {
        mp[find(i)].first = max(mp[find(i)].first, Size[i]);
        mp[find(i)].second += Size[i];
    }
    for (auto t : mp)//分组背包
    {
        for (int i = m; i >= 0; i--)
        {
            for (int j = t.second.first; j <= t.second.second; j++)
            {
                if (i >= j)
                    f[i] = max(f[i], f[i - j] + j);
            }
        }
    }
    cout << f[m] << endl;
    return 0;
}

最后

以上就是瘦瘦大神为你收集整理的G - Outing(tarjan缩点+依赖背包)的全部内容,希望文章能够帮你解决G - Outing(tarjan缩点+依赖背包)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(49)

评论列表共有 0 条评论

立即
投稿
返回
顶部