POJ 1149 PIGS 最大流

PIGS
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 20272 Accepted: 9255

Description

Mirko works on a pig farm that consists of M locked pig-houses and Mirko can't unlock any pighouse because he doesn't have the keys. Customers come to the farm one after another. Each of them has keys to some pig-houses and wants to buy a certain number of pigs. 
All data concerning customers planning to visit the farm on that particular day are available to Mirko early in the morning so that he can make a sales-plan in order to maximize the number of pigs sold. 
More precisely, the procedure is as following: the customer arrives, opens all pig-houses to which he has the key, Mirko sells a certain number of pigs from all the unlocked pig-houses to him, and, if Mirko wants, he can redistribute the remaining pigs across the unlocked pig-houses. 
An unlimited number of pigs can be placed in every pig-house. 
Write a program that will find the maximum number of pigs that he can sell on that day.

Input

The first line of input contains two integers M and N, 1 <= M <= 1000, 1 <= N <= 100, number of pighouses and number of customers. Pig houses are numbered from 1 to M and customers are numbered from 1 to N. 
The next line contains M integeres, for each pig-house initial number of pigs. The number of pigs in each pig-house is greater or equal to 0 and less or equal to 1000. 
The next N lines contains records about the customers in the following form ( record about the i-th customer is written in the (i+2)-th line): 
A K1 K2 ... KA B It means that this customer has key to the pig-houses marked with the numbers K1, K2, ..., KA (sorted nondecreasingly ) and that he wants to buy B pigs. Numbers A and B can be equal to 0.

Output

The first and only line of the output should contain the number of sold pigs.

Sample Input

3 3
3 1 10
2 1 2 2
2 1 3 3
1 2 6

Sample Output

7

题意:有很多个猪圈(编号1-m),每个猪圈有若干头猪,开始所有猪圈的门是关着的。一次来了n个顾客,每个顾客会打开指定的几个猪圈,从中买若干头猪。每个顾客有买的数量上限,买好之后,对打开着的猪圈可以对每个猪圈猪的数量进行重新分配。问最多能卖出去多少猪。

解法:问题的关键就是那个重新分配,要是不重新分配,直接s连猪圈,顾客连t,谁能买谁连起来直接跑dinic就行了。就是这个重新分配,然后我想把能互相分配的点建成双向边,但是问题就是,顾客的选购又是有时间顺序的,这样跑出来答案显然是错的。那么就只能新建节点,每个顾客对应m个新的猪圈,首先新建s到每个猪圈的边(s,i',w),w为猪圈容量,每个人到汇点的边(p,t,w)。然后第一个人p1与能开的的猪圈连边,这些能开的猪圈到下一个人面对的所有猪圈相同编号的点连一个像完全图一样的边,跑最大流,就是答案。然而这样搞下来点太多了。想着想着还是只有靠人家论文的方法,论文链接。这个建模方法太腻害了,我能懂,但描述不来,然后跟着那个建图方法过了这个题,顺便记下3个很重要的规律。


规律 1. 如果几个结点的流量的来源完全相同,则可以把它们合并成一个。 

规律 2. 如果几个结点的流量的去向完全相同,则可以把它们合并成一个。 

规律 3. 如果从点 u 到点 v 有一条容量为∞的边,并且点 v 除了点 u 以外没 有别的流量来源,则可以把这两个结点合并成一个。 


其实我只是想把我思考的过程写下来。

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <queue>
using namespace std;
const int N = 2000+10;
const int INF = 1e8;
struct node{
    int v,flow,next;
    node(){}
    node(int v,int flow,int next):
        v(v),flow(flow),next(next){}
}E[N*10];
int s,t;
int n,m,top;
int head[N];
int pig[N];  ///每个猪圈的容量
int num[N];  ///当前猪圈人的编号
int d[N];    ///bfs的dis
bool vis[N]; ///bfs标记

void Init()
{
    top = 0;
    for(int i = 0;i < N;i++){
        head[i] = -1;
        num[i] = 0;
    }
}

void add(int u,int v,int flow)
{
    E[top] = node(v,flow,head[u]);
    head[u] = top++;
    E[top] = node(u,0,head[v]);
    head[v] = top++;
}

bool bfs()
{
    memset(vis,false,sizeof vis);
    memset(d,0,sizeof d);
    queue<int>q;
    vis[s] = true;
    q.push(s);
    while(!q.empty()){
        int u = q.front();
        q.pop();
        for(int i = head[u];i != -1;i = E[i].next){
            int v = E[i].v;
            if(vis[v] || E[i].flow == 0) continue;
            vis[v] = true;
            d[v] = d[u]+1;
            q.push(v);
        }
    }
    return vis[t];
}

int dfs(int u,int a)
{
    if(u == t || a == 0){
        return a;
    }
    int flow = 0;
    for(int i = head[u];i != -1;i = E[i].next){
        int v = E[i].v;
        if(d[v] == d[u]+1 && E[i].flow){
            int f = dfs(v,min(a,E[i].flow));
            flow += f;
            a -= f;
            E[i].flow -= f;
            E[i^1].flow += f;
        }
    }
    return flow;
}

int dinic()
{
    int flow = 0;
    while(bfs()){
        flow += dfs(s,INF);
    }
    return flow;
}

int main(void)
{
    s = 150,t = 151;
    while(scanf("%d%d",&n,&m) != EOF){
        Init();
        for(int i = 1;i <= n;i++){
            scanf("%d",&pig[i]);
        }
        for(int i = 1;i <= m;i++){
            int pi;
            scanf("%d",&pi);
            for(int j = 1;j <= pi;j++){
                int p;
                scanf("%d",&p);
                if(num[p]){   ///猪圈被访问过就建上一个人到这个人的边
                    add(num[p],i,INF);
                    num[p] = i;
                }
                else{         ///没访问过就建源点过来的边
                    num[p] = i;
                    add(s,i,pig[p]);
                }
            }
            scanf("%d",&pi);
            add(i,t,pi);
        }
        int ans = dinic();
        printf("%d\n",ans);
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值