最大流&&最小割&&最大权闭子图题集

链接:https://vjudge.net/problem/POJ-3281

题意:

一头牛需要吃1个drink和1个food,给定牛的喜好,每个food和drink只能给1头牛吃,问最多满足多少头牛

解析:

匹配问题,第一个想到是二分图匹配,但是这里要匹配两个东西,所以二分图匹配无效

解法为最大流.

如果建图为:food->牛->drink,解决了一个食物饮料给1头牛吃,但是无法解决牛吃多个食物,多个drink

正确的建图为:food->牛->牛'->drink,这样就保证牛只会吃一个food和drink

网络流建图才是关键

0,2*n+f+d+1分别为源点和汇点

1~f为food,   f+1~2*n+f为牛与niu',  2*n+f+1~2*n+f+d为drink.

ac:

#include <cstdio>
#include<iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
 
const int MAXN = 1005;
const int inf = 1e9 + 7;
 
int G[MAXN][MAXN],layer[MAXN], G1[MAXN][MAXN];
int P, N;
 
struct node
{
    int in[MAXN],
        out[MAXN],
        flow;
}a[MAXN];
 
void init()
{
    memset(G, 0, sizeof G);
    memset(G1, 0 ,sizeof G1);
    for (int i = 1; i <= P; i++)
    {
        a[1].out[i] = 0;
        a[1].in[i] = 0;
        a[N + 2].in[i] = 1;
        a[N + 2].out[i] = 1;
    }
    a[1].flow = inf;
    a[N + 2].flow = inf;
}
 
bool BFS(int front, int tail) {
    int vis[MAXN] = {0};
    queue<int> que;
    que.push(front);
    memset(layer,-1,sizeof(layer));
    vis[front] = 1;
    layer[front] = 0;
 
    while (!que.empty()){
        int u=que.front();
        que.pop();
        if (u == tail) return true;
        for(int i=1;i<=tail;i++){
            if (G[u][i] && !vis[i]){
                vis[i] = true;
                layer[i] = layer[u] + 1;
                que.push(i);
            }
        }
    }
    return false;
}
 
int dfs(int u, int MaxFlow, int tail){
    if (u == tail) return MaxFlow;
    int uFlow = 0;
    for (int i = 0; i<= tail; i++){
        if (layer[u] + 1 == layer[i] && G[u][i]){
            int flow = min(MaxFlow - uFlow , G[u][i]);
            flow = dfs(i, flow, tail);
            G[u][i] -= flow;
            G[i][u] += flow;
            uFlow += flow;
            if (uFlow == MaxFlow) break;
        }
    }
    return uFlow ;
}
 
int dinic(int front, int tail){
    int MaxFlow = 0;
    while (BFS(front, tail))
        MaxFlow += dfs(front, inf, tail);
    return MaxFlow;
}
 
int main()
{
    int n,f,d,x,y,c;
    scanf("%d%d%d",&n,&f,&d);
    int st=0,ed=f+2*n+d+1;
 
    for(int i=1;i<=f;i++)
        G[st][i]=1;
    for(int i=1;i<=n;i++)
        G[i+f][i+n+f]=1;
    for(int i=1;i<=d;i++)
        G[2*n+f+i][ed]=1;
 
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&x,&y);
        while(x--)
        {
            scanf("%d",&c);
            G[c][f+i]=1;
        }
        while(y--)
        {
            scanf("%d",&c);
            G[i+n+f][2*n+f+c]=1;
        }
    }
    printf("%d\n",dinic(st,ed));
}

 

链接:https://codeforces.com/gym/101981/attachments

题意:

有n个英雄,m个野怪,k瓶药水

1个英雄只能打1个野怪

野怪只能被打死1次

k瓶药,可以让一个英雄额外打死一个野怪

一个英雄最多吃1瓶药

问最多打死几个野怪?

解析:

最大流问题,模板题

建图方法:

源点->(1)英雄->(2)英雄'->(1)野怪->(1)野怪'->(1)终点

源点->(k)药->(1)英雄

ac:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
#define MAXN 2100
using namespace std;

/* n,m,k  英雄数目,野怪数,药水数
   ti ti个mij
*/

int G[MAXN][MAXN];
const int inf = 1e9 + 7;

int layer[MAXN], G1[MAXN][MAXN];
int P, N;

struct node
{
    int in[MAXN],
        out[MAXN],
        flow;
}a[MAXN];

void init()
{
    memset(G, 0, sizeof G);
    memset(G1, 0 ,sizeof G1);
    for (int i = 1; i <= P; i++)
    {
        a[1].out[i] = 0;
        a[1].in[i] = 0;
        a[N + 2].in[i] = 1;
        a[N + 2].out[i] = 1;
    }
    a[1].flow = inf;
    a[N + 2].flow = inf;
}

bool BFS(int front, int tail) {
    int vis[MAXN] = {0};
    queue<int> que;
    que.push(front);
    memset(layer,-1,sizeof(layer));
    vis[front] = 1;
    layer[front] = 0;

    while (!que.empty()){
        int u=que.front();
        que.pop();
        if (u == tail) return true;
        for(int i=1;i<=tail;i++){
            if (G[u][i] && !vis[i]){
                vis[i] = true;
                layer[i] = layer[u] + 1;
                que.push(i);
            }
        }
    }
    return false;
}

int dfs(int u, int MaxFlow, int tail){
    if (u == tail) return MaxFlow;
    int uFlow = 0;
    for (int i = 0; i<= tail; i++){
        if (layer[u] + 1 == layer[i] && G[u][i]){
            int flow = min(MaxFlow - uFlow , G[u][i]);
            flow = dfs(i, flow, tail);
            G[u][i] -= flow;
            G[i][u] += flow;
            uFlow += flow;
            if (uFlow == MaxFlow) break;
        }
    }
    return uFlow ;
}

int dinic(int front, int tail){
    int MaxFlow = 0;
    while (BFS(front, tail))
        MaxFlow += dfs(front, inf, tail);
    return MaxFlow;
}

int main()
{
    int n,m,k,g,t;
    scanf("%d%d%d",&n,&m,&k);
    int st=0;//起点
    int ed=2*n+2*m+2;///终点(终点一定为坐标最大的)
    int gv=ed-1;//药水点
    ///建图
    G[st][gv]=k;//连药水
    for(int i=1;i<=n;i++)//英雄连英雄',源点连英雄,药水连英雄
    {
        G[st][i]=1;
        G[i][i+n]=2;
        G[gv][i]=1;
    }
    for(int i=1;i<=m;i++)//野怪连野怪'
    {
        G[2*n+i][2*n+m+i]=1;
        G[2*n+m+i][ed]=1;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&t);
        for(int j=1;j<=t;j++)
        {
            scanf("%d",&g);
            G[n+i][2*n+g]=1;
        }
    }
    printf("%d\n",dinic(st,ed));
    return 0;
}

hdu-6582:http://acm.hdu.edu.cn/showproblem.php?pid=6582

解析:

给定一个图,让你切断一些边,使得最短路变大或者断路

解析:

用最短路点建图求最小割

ac:

#include<bits/stdc++.h>
#define ll long long
#define MAXN 100005
using namespace std;

struct Edge
{
    ll v, w, nxt;
};

bool isFind;
ll head[MAXN];
Edge edge[MAXN<<1];
ll dis[MAXN], gap[MAXN];
ll n, m, ecnt, aug, maxFlow;
ll u[MAXN],v[MAXN],w[MAXN];
ll to[MAXN<<1],val[MAXN<<1],next1[MAXN<<1],head2[MAXN<<1];
ll tot=0;
ll dist[MAXN];


void init(){
    tot=ecnt = maxFlow = 0;
    memset(gap, 0, sizeof(gap));
    memset(dis, 0, sizeof(dis));
    memset(edge, 0, sizeof(edge));
    memset(head, -1, sizeof(head));

    memset(dist,0,sizeof(dist));
    memset(to,0,sizeof(to));
    memset(head2,0,sizeof(head2));
    memset(val,0,sizeof(val));
    memset(next1,0,sizeof(next1));
    gap[0] = n;
}

void addEdge(ll u, ll v, ll w){
    edge[ecnt].v = v;
    edge[ecnt].w = w;
    edge[ecnt].nxt = head[u];
    head[u] = ecnt++;
}

void Find(ll s)
{
    ll dx, augc;
    ll minDis;
    if(s == n){
        isFind = true;
        maxFlow += aug;
        return;
    }
    augc = aug;
    minDis = n - 1;
    for(ll i = head[s]; i + 1; i = edge[i].nxt){
        if(edge[i].w > 0){
            if(dis[s] == dis[edge[i].v] + 1){
                aug = min(aug, edge[i].w);
                Find(edge[i].v);
                if(dis[1] >= n) return;
                if(isFind){
                    dx = i;
                    break;
                }
                aug = augc;
            }
            minDis = min(minDis, dis[edge[i].v]);
        }
    }
    if(!isFind){
        gap[dis[s]]--;
        if(gap[dis[s]] == 0) dis[1] = n;
        dis[s] = minDis + 1;
        gap[dis[s]]++;
    }else{
        edge[dx].w -= aug;
        edge[dx ^ 1].w += aug;
    }
}

struct node
{
    ll to;
    ll dis;
    friend bool operator <(node a,node b)
    {
        return a.dis>b.dis;
    }
};

void add(ll u,ll v,ll x)
{
    to[++tot]=v;
    val[tot]=x;
    next1[tot]=head2[u];
    head2[u]=tot;
}

void dij(ll st)
{
    for(ll i=0;i<MAXN;i++)
        dist[i]=1e18;
    dist[st]=0;
    priority_queue<node> que;
    que.push(node{st,0});
    while(!que.empty())
    {
        ll u=que.top().to;que.pop();
        for(ll i=head2[u];i;i=next1[i])
        {
            ll v=to[i];
            if(dist[v]>dist[u]+val[i])
            {
                dist[v]=dist[u]+val[i];
                que.push(node{v,dist[v]});
            }
        }
    }
}

int main()
{
    ll t;
    scanf("%lld",&t);
    while(t--)
    {
        init();
        scanf("%lld%lld", &n, &m);
        init();
        for(ll i = 0; i < m; i++)
        {
            scanf("%lld%lld%lld", &u[i], &v[i], &w[i]);
            add(u[i],v[i],w[i]);
        }
        dij(1);
        for(ll i=0;i<m;i++)
        {
            if(dist[v[i]]==dist[u[i]]+w[i])
            {
                addEdge(u[i],v[i],w[i]);
                addEdge(v[i],u[i],0);
            }
        }
        while(dis[1]<n)
        {
            isFind = 0;
            aug = 1e18;
            Find(1);
        }
        printf("%lld\n",maxFlow);
    }
    return 0;
}

最大权闭合子图:

链接:https://ac.nowcoder.com/acm/contest/116/D

众所周知,杨老师是一位十分勤奋的老师,他非常的热爱学习。

勤奋的他为自己罗列了一个学习清单,共有n个知识点,他可以有选择的进行学习。

每个知识点都会对应0个或1个或多个先修知识点(只有学会了先修知识点才能学习该知识点),同时每个知识点都有一个智慧值和一个智力消耗值。

杨老师希望在进行过激烈的学习之后,他的收获可以·量化为所有学过的题的智慧值的和与智力消耗值的和的差值。请问,这个值最大是多少?

解析:

最大权闭合子图

经典的费用流问题,什么是最大权闭合子图呢?就是给你一个带权图,我们要取出一个连通块使之权值最大

杨老师获得的智慧-智力消耗最大=>全图中的正权点-最小割

ac:

#include<bits/stdc++.h>
#define mm1(a) memset((a),-1,sizeof((a)))
#define mm0(a) memset((a),0,sizeof((a)))
#define mmx(a) memset((a),0x3f,sizeof((a)))
using namespace std;

typedef long long LL;       //https://blog.csdn.net/qq_39599067/article/details/80178357
const int N = 100005;
const int INF = 0x3f3f3f3f;
int n,m,tot,vt,vs;
int d[N],head[N];

struct lp
{
    int to,w,nex;
    lp(){}
    lp(int a,int b,int c){to=a;w=b;nex=c;}
}cw[N<<2];

int pigNum[N],pigTo[N];
inline void add(int a,int b,int c)
{
    cw[++tot]=lp(b,c,head[a]);
    head[a]=tot;
    cw[++tot]=lp(a,0,head[b]);
    head[b]=tot;
}

bool bfs()
{
    mm1(d);
    queue<int>Q;
    Q.push(vt);d[vt]=0;
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop();
        for(int i=head[u];i!=-1;i=cw[i].nex)
        {
            int v=cw[i].to;
            if(cw[i^1].w&&d[v]==-1)
            {
                d[v]=d[u]+1;
                Q.push(v);
            }
        }
    }
    return d[vs]!=-1;
}

int dfs(int x,int f)
{
    if(x==vt||f==0) return f;
    int use=0,w;
    for(int i=head[x];i!=-1;i=cw[i].nex)
    {
        int to=cw[i].to;
        if(d[to]==d[x]-1 && cw[i].w)
        {
            w=dfs(to,min(cw[i].w,f-use));
            cw[i].w-=w,cw[i^1].w+=w;
            use+=w;
            if(use==f) return f;
        }
    }
    return use;
}

inline void init()
{
    tot=-1;
    mm1(head);
    mm0(pigTo);
    vs=0,vt=n+1;
}

int main()
{
    int x,y;
    scanf("%d",&n);
    init();
    int sum=0;
    for(int i=1;i<=n;++i)
    {
        scanf("%d%d",&x,&y);
        if(x-y>=0)
        {
            sum+=x-y;
            add(vs,i,x-y);
        }
        else{
            add(i,vt,y-x);
        }
    }
    int cc=0;
    while(~scanf("%d%d",&x,&y))
    {
        add(y,x,INF);
        cc++;
        if(cc==3)
            break;
    }
    int ans=0;
    while(bfs())
        ans+=dfs(vs,INF);
    printf("%d\n",sum-ans);
    return 0;
}

https://www.cnblogs.com/wangwangyu/p/9802513.html

费用流:https://blog.csdn.net/weixin_41183791/article/details/88956873

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值