POJ 1149 (网络流,有趣的建图)

本文介绍了一个使用网络流算法解决农夫如何最大化卖出猪的问题。通过将问题抽象为网络流模型,并详细解释了如何构建图模型来解决这个问题。具体包括如何连接源点、猪圈、顾客以及汇点等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

网络流(建图是重点)

题意:

有一个卖猪的农夫,他有n个猪圈,但是猪圈的钥匙不在他的手上,有m个顾客,每一个顾客会带一些猪圈的钥匙,每一个顾客都有自己想买猪的数量,当然他所带的钥匙能开启相应的猪圈,从而能买到相应的猪,不过有时猪比他想买的数量多,也有可能少。而农夫有一个权力,就是如果某一个顾客没有买完他开的猪圈里的猪,农夫可以把剩下的猪随意的转移在被这个顾客的开启的猪圈里面。求出农夫最多能卖多少猪?

思路:

切入点应该是“转移”字眼吧,意味着可以用网络流解决问题,因为没有花费的存在,那么就是最大流。

我们知道网络流难点是在于把问题抽象成网络,进而建图。

因为我做的网络流不是很多,不过这道题教会了我一个方法去建图,先把所有条件列在纸上,在纸上试着根据题意建一个图,根据题意的意思是有很多限制条件而重点就是在限制条件上建图,然后手动跑一边网络流看看时都能得到结果。如果能得到,那么很可能就是对的建图。

这道题的建图方式就是:猪圈的猪的限制条件是顾客,我们把源点与猪圈连成边,猪圈和顾客连成边,因为猪圈可能会有多个顾客访问,我们只连最先访问的顾客,如果有多个顾客访问这个猪圈,就把剩下的顾客与第一次访问的顾客相连成边,流量是无限大,这意味着前边顾客买不完的猪可以让后边的顾客买。

最后每一个顾客与汇点连成边流量是顾客买猪的最大值。

  • 注意点:顾客与顾客之间建图的时候,注意重边,就是顾客与顾客之间最多建一次就行了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>

using namespace std;

const int maxn = 2005;
const int INF = 0x3f3f3f3f;

struct edge
{
    int to,cap,rev;
};

int n,m;
vector<edge>G[maxn];
int pignum[maxn];
int vis[maxn];
int level[maxn],iter[maxn];

void add_edge(int from,int to,int cap)
{
    int length = G[from].size();
    int length1 = G[to].size();
    G[from].push_back((edge){to,cap,length1});
    G[to].push_back((edge){from,0,length});
}

void bfs(int s)
{
    memset(level,-1,sizeof(level));
    queue<int>Q;
    Q.push(s);
    level[s] = 0;
    while(!Q.empty()) {
        int to = Q.front();
        Q.pop();
        int length = G[to].size();
        for(int i = 0;i < length; i++) {
            edge &e = G[to][i];
            if(e.cap > 0 && level[e.to] < 0) {
                level[e.to] = level[to] + 1;
                Q.push(e.to);
            }
        }
    }
}

int dfs(int u,int t,int f)
{
    if(u == t) return f;
    else {
        int length = G[u].size();
        for(int &i = iter[u];i < length; i++) {
            edge &e = G[u][i];
            if(e.cap > 0 && level[e.to] > level[u]) {
                int d = dfs(e.to,t,min(f,e.cap));
                if(d > 0) {
                    e.cap -= d;
                    G[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
    }
    return 0;
}

int max_flow(int s,int t)
{
    int flow = 0;
    for(;;) {
        bfs(s);
        if(level[t] < 0)
            break;
        memset(iter,0,sizeof(iter));
        int f;
        while((f = dfs(s,t,INF)) > 0) {
            flow += f;
        }
    }
    return flow;
}

int main()
{
    //freopen("in.txt","r",stdin);

    scanf("%d%d",&n,&m);

    for(int i = 1;i <= n; i++) {
        scanf("%d",&pignum[i]);
        add_edge(0,i,pignum[i]);
    }
    for(int i = n+1;i <= n+m; i++) {
        int pighouse;
        scanf("%d",&pighouse);
        for(int k = 0;k < pighouse; k++) {
            int pig;
            scanf("%d",&pig);
            if(vis[pig] == 0) {
                vis[pig] = true;
                add_edge(pig,i,pignum[pig]);
            }
            else {
                int uppeople = G[pig][1].to;
                int length = G[uppeople].size();
                int flag = true;
                for(int j = 0;j < length; j++) {
                    if(G[uppeople][j].to == i) {
                        flag = false;
                        break;
                    }
                }
                if(flag) {
                    add_edge(uppeople,i,INF);
                }
            }
        }
        int sum;
        scanf("%d",&sum);
        add_edge(i,n+m+1,sum);
    }
    printf("%d\n",max_flow(0,n+m+1));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值