poj1149

PIGS
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 17297 Accepted: 7841

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

题目大意:m个猪圈,n个人先后来买猪,每个人会选择若干个猪圈购买,一定个数的猪(购买力),购买完剩余的猪可以分配到猪圈(必须是这个人选择的猪圈)。

问n个人最多能购买多少猪?

一个想法就是对猪圈按照买的人顺序拆分,方法和我之前做过的“家园”类似,传送门:家园 

但这只是想法,如果这么做图会非常庞大,这是网络流算法必定超时,于是想要合并点。

这些都是技巧问题,可以上网查查资料。

我把最后转化出的简化模型说一下:

对每个猪圈都有一个购买序列(人的编号),对于每个猪圈的第一人,他将有权支配该猪圈的所有猪,也就是从源点连接这个人,权值就是该猪圈的猪的数量;对于每个猪圈的第i(i>1)人,显然该猪圈已被前i-1人打开,因此

这i-1人都拥有将他们能支配的猪放到该猪圈的权利,换句话说,该猪圈此时可以拥有的猪的数量为这些人能支配的猪的总数,也就是从这i-1人出发,每个人连向第i人,边权为无穷大,这样表示这些人能支配的猪都可以流向第i人,最后对于每一个人都有一条边连向汇点,边权就是他们各自的购买力。然后一遍最大流即可。这里采用Edmonds_Karp算法。

代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#define Maxn 110
using namespace std;
const int inf=0x3f3f3f3f;
int cap[Maxn][Maxn];
int flow[Maxn][Maxn];
int pre[Maxn];
int alpha[Maxn];
int q[Maxn];
int m,n;
int num[Maxn*10],buy[Maxn];
vector<int> dv[Maxn*10]; //猪圈购买顾客顺序
void init(){
    memset(cap,0,sizeof cap);
    memset(flow,0,sizeof flow);
}
int Edmonds_Karp(int src,int t){
    int maxflow=0;
    while(1){
        memset(alpha,0,sizeof alpha);
        alpha[src]=inf;
        pre[src]=-1;
        int s=0,e=-1;
        q[++e]=src;
        while(s<=e&&!alpha[t]){
            int u=q[s++];
            for(int i=0;i<=n+1;i++){
                if(!alpha[i]&&flow[u][i]<cap[u][i]){
                    pre[i]=u;
                    alpha[i]=min(alpha[u],cap[u][i]-flow[u][i]);
                    q[++e]=i;
                }
            }
        }
        if(!alpha[t]) break;
        int k=t;
        while(pre[k]!=-1){
            flow[pre[k]][k]+=alpha[t];
            flow[k][pre[k]]-=alpha[t];
            k=pre[k];
        }
        maxflow+=alpha[t];
    }
    return maxflow;
}
int main()
{
    int z,x;
    while(cin>>m>>n){
        init();
        for(int i=1;i<=m;i++){
            dv[i].clear();
            scanf("%d",num+i);
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&z);
            for(int j=0;j<z;j++){
                scanf("%d",&x);
                dv[x].push_back(i);
            }
            scanf("%d",buy+i);
        }
        for(int i=1;i<=m;i++){
            cap[0][dv[i][0]]+=num[i]; //每个猪圈第一个顾客
            for(int j=1;j<dv[i].size();j++)
                for(int k=0;k<j;k++)
                    cap[dv[i][k]][dv[i][j]]=inf;
        }
        for(int i=1;i<=n;i++)
            cap[i][n+1]+=buy[i];
        printf("%d\n",Edmonds_Karp(0,n+1));
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值