I - Magic Potion ( 网络最大流 )

I - Magic Potion ( 网络最大流 )

题意:n个人, m个怪兽 ,k个buff。 接下来n行代表第i个人可以杀的怪兽编号, 每个人只能杀一只怪兽,但是如果有buff就可以杀两只怪兽,但是每个人只能用一次buff。 问最多能杀多少只怪兽。

思路:网络流, s源点连接n个人流量为1(代表每个人只能杀一只怪兽),每只怪兽连接汇点t流量为1( 代表每只怪兽只能被杀一次 ), 人和其能杀死的怪兽连接流量为1 , 跑一遍denic,得到不用buff的答案。 我们再假设这里的每个人都拿到了一个buff,所以s到相邻点的流量都变成1, 再跑一遍dinic, 这样如果跑出来的二次答案大于buff的数量那么所有buff都能贡献一个答案, 如果小于, 那么答案就是一次答案加二次答案。

代码:

#include <iostream>  // 网络最大流
#include <queue>
#include <stdio.h>
#include <cstring>
#include <map>
#define inf 0x3f3f3f3f

using namespace std;

struct node {
    int to,w,nxt;
};
const int maxn = 2e5+10;
node e[maxn];
int n,m,s,t,k;
int head[maxn];
int dep[maxn];
int cnt;

void addage( int u, int v, int w )
{
    e[cnt].to = v;
    e[cnt].w = w;
    e[cnt].nxt = head[u];
    head[u] = cnt++;
}

int bfs(int node) // bfs分层
{
    memset(dep,0,sizeof(dep)); // dep 层的深度
    dep[node] = 1;
    queue <int> Q;
    Q.push(node);
    while ( !Q.empty() ) {
        int x = Q.front();Q.pop();
        for ( int i=head[x]; i!=-1; i=e[i].nxt ) {
            int y = e[i].to,w = e[i].w;
            if ( !dep[y] && w ) {
                dep[y] = dep[x] + 1; // 编号++
                Q.push(y);
            }
        }
    }
    return dep[t]; // return dep[t] , 如果是0那么说明没有增广路了,退出while
}

int dfs( int x, int flow ) // dfs找增广路
{
    if ( x==t ) {
        return flow;
    }
    int sum = 0;
    for ( int i=head[x]; i!=-1; i=e[i].nxt ) {
        int y = e[i].to, w = e[i].w;
        if ( w && dep[y]==dep[x]+1 ) {
            int t = dfs(y,min(flow,w));// 注意dfs里面的是min(flow,w)
            flow -= t;      // 当前的flow减t
            sum+=t;         // sum加t,就是上面减去的t。
            e[i].w -= t;    // 正向边减t
            e[i^1].w += t;  // 反向边加t
        }
    }
    if ( sum==0 ) { // 如果sum==0,那么这个点之前没有增广路,深度清零
        dep[x] = 0;
    }
    return sum;
}

int main()
{
    int i,j,x;
    memset(head,-1,sizeof(head));
    cnt=0;
    // 人 1-500, 怪兽500+i,
    s = 0; t = 1010;
    scanf("%d %d %d",&n,&m,&k);
    for ( i=1; i<=n; i++ ) {
        addage(s,i,1);
        addage(i,s,0);
        int tt;
        scanf("%d",&tt);
        for ( j=1; j<=tt; j++ ) {
            scanf("%d",&x);
            addage(i,500+x,1);
            addage(500+x,i,0);
        }
    }
    for ( i=1; i<=m; i++ ) {
        addage( 500+i,t,1 );
        addage( t,500+t,0 );
    }

    int ans = 0;
    while ( bfs(s) ) { // 如果还存在增广路继续
        ans += dfs(s,0x3f3f3f3f); // 加一条增广路的值
    }

    for ( i=head[s]; i!=-1; i=e[i].nxt ) {
        e[i].w = 1;
    }
    int tot = 0;
    while ( bfs(s) ) { // 如果还存在增广路继续
        tot += dfs(s,0x3f3f3f3f); // 加一条增广路的值
    }
    if ( tot>=k ) {
        ans += k;
    }
    else {
        ans += tot;
    }

    printf("%d\n",ans);

    return 0;
}

/*
5 10 2
2 3 10
5 1 3 4 6 10
5 3 4 6 8 9
3 1 9 10
5 1 3 6 7 10
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值