hihocoder #1398 : 网络流五·最大权闭合子图

标签: hihocoder


描述

周末,小Hi和小Ho所在的班级决定举行一些班级建设活动。
根据周内的调查结果,小Hi和小Ho一共列出了N项不同的活动(编号1..N),第i项活动能够产生a[i]的活跃值。
班级一共有M名学生(编号1..M),邀请编号为i的同学来参加班级建设活动需要消耗b[i]的活跃值。
每项活动都需要某些学生在场才能够进行,若其中有任意一个学生没有被邀请,这项活动就没有办法进行。
班级建设的活跃值是活动产生的总活跃值减去邀请学生所花费的活跃值。
小Hi和小Ho需要选择进行哪些活动,来保证班级建设的活跃值尽可能大。
比如有3项活动,4名学生:
第1项活动产生5的活跃值,需要编号为1、2的学生才能进行;
第2项活动产生10的活跃值,需要编号为3、4的学生才能进行;
第3项活动产生8的活跃值,需要编号为2、3、4的学生才能进行。
编号为1到4的学生需要消耗的活跃值分别为6、3、5、4。
假设举办活动集合为{1},需要邀请的学生集合为{1,2},则得到的班级活跃值为5-9 = -4。
假设举办活动集合为{2},需要邀请的学生集合为{3,4},则得到的班级活跃值为10-9 = 1。
假设举办活动集合为{2,3},需要邀请的学生集合为{2,3,4},则得到的班级活跃值为18-12 = 6。
假设举办活动集合为{1,2,3},需要邀请的学生集合为{1,2,3,4},则得到的班级活跃值为23-18 = 5。
小Hi和小Ho总是希望班级活跃值越大越好,因此在这个例子中,他们会选择举行活动2和活动3。
提示:最大权闭合子图

输入

第1行:两个正整数N,M。1≤N≤200,1≤M≤200
第2行:M个正整数,第i个数表示邀请编号为i的学生需要花费的活跃值b[i],1≤b[i]≤1,000
第3..N+2行:第i行表示编号为i的活动情况。首先是2个整数a,k,a表示该活动产生的活跃值,k表示该活动需要的学生人数。接下来k个整数列举该活动需要的学生编号。1≤a≤1,000,1≤k≤M

输出

第1行:1个整数,最大可以产生的班级活跃值

样例输入

3 4
6 3 5 4
5 2 1 2
10 2 3 4
8 3 2 3 4

样例输出

6

分析

最大权闭合子图模板题.
最大的闭合子图权值=权值为正的节点的权值和-网络最小割

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=450;
const int inf=0x3f3f3f3f;
struct Edge
{
    int v,nxt,c,f;
}e[maxn*maxn*2];
int h[maxn],tot=1;
void addEdge(int x,int y,int w){
    e[++tot]=(Edge){y,h[x],w,0};
    h[x]=tot;
}
int n,m,s,t;
bool vis[maxn];
int dfs(int x,int flow){
    if(vis[x]||flow==0) return 0;
    vis[x]=true;
    if(x==t) return flow;
    int ans=0;
    for(int i = h[x]; i ; i=e[i].nxt){
        int t=min(flow,e[i].c-e[i].f);
        if(t>0 && (ans=dfs(e[i].v,t))){
            e[i].f+=ans;
            return ans;
        }
        t=min(flow,e[i^1].f);
        if(t>0 && (ans=dfs(e[i].v,t))){
            e[i^1].f-=ans;
            return ans;
        }
    }
    return 0;
}
int main(int argc, char const *argv[])
{
    scanf("%d%d", &n,&m);
    s=0,t=n+m+1;
    for(int i = n+1; i<= n+m; ++i){
        int x;
        scanf("%d", &x);
        addEdge(i,t,x);
        addEdge(t,i,0);
    }
    int M=0;
    for(int i = 1; i <= n; ++i){
        int x,k;
        scanf("%d%d", &x,&k);
        M+=x;
        addEdge(s,i,x);
        addEdge(i,s,0);
        while(k--){
            scanf("%d", &x);
            addEdge(i,x+n,inf);
            addEdge(x+n,i,0);
        }
    }
    int sum=0,flow=0;
    while(flow=dfs(s,inf)){
        sum+=flow;
        memset(vis,false,sizeof vis);
    }
    printf("%d\n", M-sum);
    return 0;
}

转载于:https://www.cnblogs.com/sciorz/p/9046721.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值