POJ 1149 PIGS (网络流)

点我查看原题链接
这道题的模型我们首先来简化一下:顾客来养猪场打开他有钥匙的猪圈,之后买走一些猪,这之后老板可以重新调整已经打开的猪圈的内猪的数量。然后下一个顾客来……
假设就这个流程建图,我们可以这样建:
1.首先对于每一轮顾客,每个猪圈就对应一个点,首先源点和第一轮的所有猪圈连接,容量是初始数量。
2.之后每一轮的猪圈i都向下一轮的猪圈i连一条边,容量INF,这一轮被打开的猪圈,向其他被打开的猪圈在下一轮的节点连一条容量无线的边,表示这些猪圈里的猪可以流通。
3.每个顾客在他对应的轮里,连接他能打开的所有猪圈,容量INF,每个顾客再向汇点连一条边,容量是他的购买上限。
这样求下来的最大流就是答案。但是这样建图明显边太多了,以下是一个简化网络流模型的通则:
1.如果有若干个节点的来源都相同,那么他们可以变成一个节点
2.如果有若干个节点的去向都相同,他们也可以变成一个
3.如果有两个节点u,v,他们之间存在边,边的方向是u到v,而且边的容量是INF同时u是v的唯一来源,那么这两个点可以当作一个
有了这三个法则,我们现在谈简化后的模型。
每个顾客用一个点表示,先从源点连一条边,这条边的容量是所有这个顾客能打开的猪圈中猪的综合
如果一个猪圈被打开了n次,对于所有的顾客都向他的下一个顾客连一条边。
每个顾客再向汇点连一条边,容量是购买上限。
这样就减少了大部分的节点,笔者建议画一下这个简化过程,很有助于理解:

#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#define clr(x) memset(x,0,sizeof(x))
using namespace std;
const int maxn = 1001;
const int INF = 0x7f7f7f7f;
int n,m;

struct edge{int to,cap,rev;};
vector<edge>G[maxn];
bool used[maxn];
int enterloop[maxn][maxn];
bool getthrough[maxn][maxn];
int pigwant[maxn];
int sink,sunk;
int pig[1001];
int first[1010];
void add_edge(int from,int to,int cap)
{
    G[from].push_back((edge){to,cap,G[to].size()});
    G[to].push_back((edge){from,0,G[from].size()-1});
}
int dfs(int v,int t, int f)
{
    if(v==t) return f;
    used[v]=true;
    for(int i=0;i<G[v].size();i++)
    {
        edge& e=G[v][i];
        if(!used[e.to]&&e.cap>0)
        {
            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;
    while(true)
    {
        memset(used,0,sizeof(used));
        int f=dfs(s,t,INF);
        if(f==0) return flow;
        flow+=f;
    }
}
void make_map()
{
    int start=0;
    int end=m+1;
    for(int i=1;i<=n;i++)
    {
        add_edge(start,first[i],pig[i]);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<enterloop[i][0];j++)
        {
            if(!getthrough[enterloop[i][j]][enterloop[i][j+1]])
            {
                add_edge(enterloop[i][j],enterloop[i][j+1],INF);
                getthrough[enterloop[i][j]][enterloop[i][j+1]]=true;
            }
        }
    }
    for(int i=1;i<=m;i++)
    {
        add_edge(i,end,pigwant[i]);
    }
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    while(cin>>n>>m)
    {
        sink = 0;
        sunk = m+1;
        memset(first,0,sizeof(first));
        for(int i=1;i<=n;i++)
        {
            cin>>pig[i];
            //sink+=pig[i];
        }
        int a,b,k;
        for(int i = 1;i<=m;i++)
        {
            cin>>a;
            while(a--)
            {
                cin>>k;

            enterloop[k][++enterloop[k][0]]=i;
            if(first[k]==0)
                first[k]=i;
            }
            cin>>pigwant[i];
            //add_edge(i,sunk,b);
        }
        make_map();
        cout<<max_flow(sink,sunk)<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值