首页 > 其他分享 >最大权闭合子图

最大权闭合子图

时间:2023-04-19 21:57:41浏览次数:45  
标签:最大 idx int sum 子图 闭合 点集

最大权闭合子图

一、概念

闭合子图

给定一个有向图\(G(V,E)\),图中的某一个点集,这个点集满足内部的所有边不能从点集里面指向点集外面,则将这个点集和点集里面的边统称为原图的闭合子图
image

最大权闭合子图

给定一个有向图\(G(V,E)\),每个点上有一个权值,图中存在若干个闭合子图,若其中一个闭合子图的点集权值和最大,则这个闭合子图称为最大权闭合子图

简单割

割边都与源点或汇点相连的割(概念只针对于本篇问题)

二、算法流程

建图

新建一个源点\(S\)和汇点\(T\),从源点\(S\)向\(w_i>0\)的点连一条边权为\(w_i\)的边,从所有\(w_i<0\)的点向汇点\(T\)连一条边权为\(-w_i\)的边,原图中点与点之间的边不变,流量设为\(+\infty\)
image

计算

对新图求一遍最小割,最大权\(=\sum_{w_i>0}w_i-\)最小割

三、算法证明

证明新图中的最小割是简单割

\(Proof\)

最小割\(=\)最大流,且新图中最大流是有限的
\(\Rightarrow\)最小割的割边必然不会包含图中\(+\infty\)的边
\(\Rightarrow\)割边都与源点或汇点相连
\(\Rightarrow\)最小割是简单割

证明原图中闭合子图和新图中的简单割一一对应

\(Proof\)

闭合子图\(\rightarrow\)简单割

新图的点集记为\(V\),对原图中的一个闭合子图,点集记为\(V'\)

构造割集\([S,T]\),其中\(S=V'+\{s\}\), \(T=V-S\)

由闭合子图的定义,\(S\)中除\(s\)外的点不存在边与\(T\)中除\(t\)外的点相连

因此割边都与源点或汇点相连

所以构造的割是简单割

简单割\(\rightarrow\)闭合子图

对于一个简单割\([S,T]\),构造闭合子图\(G(V,E)\),其中\(V=S-\{s\}\)

由于简单割保证了两个集合之间不存在相连的边,所以从\(S\)的任何一个点出发,都必然会走回到\(S\),不可能走到\(T\)

因此\(V\)是一个闭合点集,\(G\)是一个闭合子图

证明最大权\(=\sum_{w_i>0}w_i-\)最小割

\(Proof\)

对任意一个简单割\([S,T]\),记对应的闭合子图点集\(V_1=S-\{s\}\),记\(V_2=V-V_1\)

\(X^+\)表示\(X\)中权值为正的点,\(X^-\)表示\(X\)中权值为负的点

则简单割的容量\(c[S,T]=\sum_{u{\in}V_2^+}w_u+\sum_{u{\in}V_1^-}(-w_u)\)

闭合子图的权值和 \(W=\sum_{u{\in}V_1^+}w_u-\sum_{u{\in}V_1^-}(-w_u)\)

两式相加得 \(W+c[S,T]=\sum_{u{\in}V^+}w_u\)

即 \(W=\sum_{u{\in}V^+}w_u-c[S,T]\)

\(\sum_{u{\in}V^+}w_u\)固定不变,要使\(W\)最大,就是要\(c[S,T]\)最小,因此\([S,T]\)为最小割

四、示例代码

#include <bits/stdc++.h>

using namespace std;

const int N = 55010, M = 310010, inf = 0x3f3f3f3f;

int n, m, S, T;
int h[N], e[M], c[M], ne[M], idx;
int q[N], dep[N], cur[N];

void add(int u, int v, int w)
{
    e[idx] = v, c[idx] = w, ne[idx] = h[u], h[u] = idx ++ ;
    e[idx] = u, c[idx] = 0, ne[idx] = h[v], h[v] = idx ++ ;
}

bool bfs()
{
    memset(dep, -1, sizeof dep);
    int hh = 0, tt = 0;
    q[0] = S, dep[S] = 0;
    cur[S] = h[S];
    while (hh <= tt)
    {
        int t = q[hh ++ ];
        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if (dep[j] == -1 && c[i])
            {
                dep[j] = dep[t] + 1;//建立分层图
                cur[j] = h[j];
                if (j == T)return true;
                q[++ tt] = j;
            }
        }
    }
    return false;
}

int find(int u, int limit)//寻找增广路
{
    if (u == T)return limit;
    int flow = 0;
    for (int i = cur[u]; ~i && flow < limit; i = ne[i])
    {
        cur[u] = i;//当前弧优化
        int j = e[i];
        if (dep[j] == dep[u] + 1 && c[i])
        {
            int t = find(j, min(c[i], limit - flow));
            if (!t)dep[j] = -1;//费点优化
            c[i] -= t, c[i ^ 1] += t, flow += t;
        }
    }
    return flow;
}

int dinic()
{
    int res = 0, flow;
    while (bfs()) while (flow = find(S, inf))res += flow;
    return res;
}

int init()
{
    int t = 0;
    scanf("%d%d", &n, &m);
    S = 0, T = n + m + 1;
    memset(h, -1, sizeof h);
    for (int i = 1; i <= n; i ++ )
    {
        int p;
        scanf("%d", &p);
        add(i, T, p);
    }
    for (int i = n + 1; i <= n + m; i ++ )
    {
        int a, b, cc;
        scanf("%d%d%d", &a, &b, &cc);
        add(i, a, inf), add(i, b, inf), add(S, i, cc);
        t += cc;
    }
    return t;
}

int main()
{
    printf("%d", init() - dinic());
    return 0;
}

参考文献

https://www.acwing.com/file_system/file/content/whole/index/content/6250083/

https://blog.csdn.net/qq_41730082/article/details/104526447

标签:最大,idx,int,sum,子图,闭合,点集
From: https://www.cnblogs.com/sjwsjw/p/17332915.html

相关文章

  • 力扣---1043. 分隔数组以得到最大和
     给你一个整数数组arr,请你将该数组分隔为长度最多为k的一些(连续)子数组。分隔完成后,每个子数组的中的所有值都会变为该子数组中的最大值。返回将数组分隔变换后能够得到的元素最大和。本题所用到的测试用例会确保答案是一个32位整数。 示例1:输入:arr=[1,15,7,9,2......
  • 力扣---1071. 字符串的最大公因子
    对于字符串s和t,只有在s=t+...+t(t自身连接1次或多次)时,我们才认定“t能除尽s”。给定两个字符串str1和str2。返回最长字符串x,要求满足x能除尽str1且X能除尽str2。示例1:输入:str1="ABCABC",str2="ABC"输出:"ABC"示例2:输入:str1="ABABAB",str2=......
  • 二分图最大匹配
    #include<bits/stdc++.h>usingnamespacestd;constintN=2e5+10;inte[N],h[N],ne[N],idx;voidadd(inta,intb){e[++idx]=b,ne[idx]=h[a],h[a]=idx;}intco[N];boolcheck(intu,intc){co[u]=c;for(inti......
  • 自定义Mybatis-plus插件(限制最大查询数量)
    自定义Mybatis-plus插件(限制最大查询数量)需求背景​ 一次查询如果结果返回太多(1万或更多),往往会导致系统性能下降,有时更会内存不足,影响系统稳定性,故需要做限制。解决思路1.经分析最后决定,应限制一次查询返回的最大结果数量不应该超出1万,对于一次返回结果大于限制的时候应该......
  • 【每日一题】分隔数组以得到最大和
    1043.分隔数组以得到最大和关键词:动态规划、递归题目来源:1043.分隔数组以得到最大和-力扣(Leetcode)题目描述T动态规划T递归给你一个整数数组arr,请你将该数组分隔为长度最多为k的一些(连续)子数组。分隔完成后,每个子数组的中的所有值都会变为该子数组中的最大值。......
  • 洛谷P1249最大乘积,数论找规律
    最大乘积题目描述一个正整数一般可以分为几个互不相同的自然数的和,如\(3=1+2\),\(4=1+3\),\(5=1+4=2+3\),\(6=1+5=2+4\)。现在你的任务是将指定的正整数\(n\)分解成若干个互不相同的自然数的和,且使这些自然数的乘积最大。输入格式只一个正整数\(n\),(\(3\leqn\leq10000\))。......
  • 动态规划:剑指 Offer 63. 股票的最大利润
    题目描述:假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少? 限制:0<=数组长度<=10^5    classSolution{publicintmaxProfit(intprices[]){//状态定义:dp[i]记为利润profit//前i......
  • 节点与其祖先之间的最大差值(树,二叉树,深度优先搜索)
    1、节点与其祖先之间的最大差值(难度中等)给定二叉树的根节点root,找出存在于不同节点A和B之间的最大值V,其中V=|A.val-B.val|,且A是B的祖先。(如果A的任何子节点之一为B,或者A的任何子节点是B的祖先,那么我们认为A是B的祖先)/***Definitionforabinary......
  • LeetCode Top100: 二叉树的最大深度 (python)
     给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。说明: 叶子节点是指没有子节点的节点。示例:给定二叉树 [3,9,20,null,null,15,7],3/\920/\157返回它的最大深度 3。 以下是Python代码实现:cl......
  • 239. 滑动窗口最大值
    设计单调栈classSolution{classMyQueue{Deque<Integer>deque=newLinkedList<>();//弹出元素时,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出publicvoidpoll(intval){if(!deque.isEmpty()&&val==deque.pee......