第一周集训总结

自我感觉: 不太行,可能是因为这周学的图论吧,没怎么接触过。


知识点总结:匈牙利算法

模板

bool Find(int v)
{
    for (int i = 1; i <= n; i ++ )
    {
        if (map[i][v] && road[i] != T)
        {
            road[i] = T;
            if (link[i] == 0 || Find(link[i]))
            {
                link[i] = v;
                return true;
            }
        }
    }
    return false;
}

大致理解:在邻接矩阵中找到与 $v$ 匹配的点,如果找到的点 $i$ 没有被匹配过,就与 $v$ 匹配上。如果找到的点 $i$ 已经与其他的点 $link_i$ 匹配上了,就看看与找到的点 $i$ 匹配的点 $link_i$ 能不能再找一个其他点匹配。如果能找到就把 $v$ 与 $i$ 匹配上。

其实还挺容易理解的。

知识点总结:Tarjan算法

模板

void Tarjan(int v)
{
    dfn[v] = low[v] = ++ cnt;
    s.push_back(v), instack[v] = true;
    for (int i : g[v])
    {
        if (!dfn[i])
        {
            Tarjan(i);
            low[v] = min(low[v], low[i]);
        }
        else if (instack[i])
        {
            low[v] = min(low[v], dfn[i]);
        }
    }
    if (dfn[v] == low[v])
    {
        int i;
        scc ++ ;
        do
        {
            i = s.back();
            s.pop_back();
            instack[i] = false;
            id[i] = scc;
        }
        while (v != i);
    }
}

数组解释: 用 $dfn_i$ 表示编号为 $i$ 的节点在 DFS 过程中的访问顺序 号(也可以叫做开始时间或者时间戳)

用 $low_i$ 表示 $i$ 节点及其下方节点所能到达的开始时间 最早的节点的开始时间。初始时 $dfn_i = low_i$

用 $instack_i$ 表示编号为 $i$ 的节点在 DFS 过程中是否入栈(被算过)

Tarjan中的缩点很重要,在套其他算法模板时常用,比如拓扑排序

其他自学知识点:并查集

void init(int x)//初始化函数,对录入的 n个结点进行初始化 
{
    for (int i = 1; i <= x; i ++ )
    {
        a[i] = i;//每个结点的上级都是自己 
        t[i] = 1;//每个结点构成的树的高度为 1 
    }
}

int find(int x)//改进查找算法:完成路径压缩,将 x的上级直接变为根结点,那么树的高度就会大大降低 
{
    if (a[x] == x) return x;//递归出口:x的上级为 x本身,即 x为根结点 
    return a[x] = find(a[x]);//此代码相当于先找到根结点 rootx,然后 a[x]=rootx
}

bool join(int x, int y) //合并x、y
{
    x = find(x);//寻找 x的代表元
    y = find(y);//寻找 y的代表元
    if (x == y) return false;//如果 x 和 y 的代表元一致,说明他们共属同一集合,则不需要合并,返回 false,表示合并失败;否则,执行下面的逻辑
    if (t[x] > t[y]) a[y] = x;
    else
    {
        if (t[x] == t[y]) t[y] ++ ;//如果 x的高度和 y的高度相同,则令 y的高度加1
        a[x] = y;//让 x 的上级为 y
    }
    return true;//返回 true,表示合并成功
}

bool isSame(int x, int y)//判断两个结点是否连通
{
    return find(x) == find(y);//判断两个结点的根结点(即代表元)是否相同 
}

挺简单的

其他自学知识点:拓扑排序

    for (int i = 1; i <= n; i ++ )//寻找入度为零的点;
    {
        if (!in[i])//入度为0;
        {
            sum[i] = 1;
            q.push(i);//加到队列当中;
        }
    }
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        for (int i : g[u])//枚举以该点为起点的所有线段的终点;
        {
            in[i] -- ;//将这个点的入度-1(因为目前要删除第u个点);
            sum[i] = (sum[i] + sum[u]) % mod;//更新到下一个点的路径数量;
            if (!in[i])
            {
                q.push(i);//如果这个点的入度为零了,那么加入队列
            }
        }
    }

也挺好理解

posted @ 2023-07-08 13:12  emo_male_god  阅读(8)  评论(0)    收藏  举报  来源