算法设计与分析 第五次编程作业 1203. 卖猪

题目描述

你有 m m m个猪圈,每个猪圈中有若干头猪。 有 n n n个顾客按顺序来买猪,每个顾客需要若干头猪。 每个顾客都有某几个猪圈的钥匙(只有顾客才拥有钥匙),只有某个猪圈打开后才能分配这个猪圈里的猪,且顾客离开后会把门关闭。
每次你可以决定卖给每个顾客哪些猪(不能多于顾客需要的数量),并且可以在一个顾客买完后,调整没有锁门的猪圈中猪数量,比如,把几个猪圈中的猪全部转到一个猪圈内,请问最后最多能卖掉几头猪。

输入格式

输入的第一行是两个整数 m m m n n n。 其中 m m m是猪圈的数量, n n n是顾客的数量。
输入的第二行有 m m m个整数,表示每个猪圈里分别有多少只猪。
接下来 n n n行,每行对应一个顾客的信息,格式如下。
第一个整数 a a a表示该顾客拥有多少把钥匙。
接下来 a a a个整数表示这些钥匙分别对应哪一些猪圈。
最后一个整数 b b b表示该顾客想买多少头猪。

输出格式

输出一个整数表示最多可以卖出多少头猪。

样例输入

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

样例输出

7

数据范围

对于 100%的数据,保证 1 ≤ m ≤ 1000 , 1 ≤ n ≤ 100 1\leq m \leq 1000, 1 \leq n \leq 100 1m1000,1n100,每个猪圈里猪的数量不超过 1000 1000 1000

时空磁盘限制(运行时)

时间限制: 1000 ms

空间限制: 62 MiB

解决方案

//思路是:把每一个顾客当作无限运力的中转站使用
//every farm connect to its first customer. 
//For latter customer to a farm, add an edge from previous customer to him with capacity INT_MAX
//(I use the array named LastCustomerOfFarm to record the last customer)
//
#include <iostream>
#include <limits.h>
#include <queue>
#include <string.h>
using namespace std;
 
const int MAXM = 1002;
const int MAXN = 102;
int m, n;
int edges[MAXM+MAXN][MAXM+MAXN];
const int s = 0;
const int t = 1102;
int parent[MAXM+MAXN]; //用于指代节点的访问顺序
bool visited[MAXM+MAXN];

bool bfs()
{
    memset(visited,0,sizeof(visited));
    queue<int> q;
    q.push(s);
    visited[s] = true;
    parent[s] = -1;

    //接下来是BFSloop
    while (!q.empty()) {
        int u = q.front();
        q.pop();

        for (int v=0;v<MAXM+MAXN;++v) {
            if (visited[v] == false && edges[u][v] > 0) {//此时找到了一个可以更新距离信息的节点
                if (v == t) {//如果v等于汇,那么说明存在一条s到t的路径,这条路径被parent数组记录下来了。只需返回即可。
                    parent[v] = u;
                    return true;
                }
                q.push(v);
                parent[v] = u;
                visited[v] = true;
            }
        }
    }
    return false; //没有到汇的路径,返回false
}

int EK()
{
    int max_flow=0;
    int u, v;
    while (bfs()) {
        int path_flow =INT_MAX;//由于w的上限是2^31-1,实在是太大了, 这里直接调用limits库的INT_MAX
        for (v = t;v != s;v = parent[v]) { //找到这条路径上最小的容量
            u = parent[v];
            path_flow = min(path_flow,edges[u][v]);
        }
        for (v = t;v != s;v = parent[v]) {
            u = parent[v];
            edges[u][v] -= path_flow;
            edges[v][u] += path_flow;
        }
        max_flow += path_flow;
        memset(parent,0,sizeof(parent));
    }
    return max_flow;
}

int main()
{
    memset(edges,0,sizeof(edges));
    
    int max_pig=0;
    cin >> m >> n;
    for (int i=1;i<=m;++i) cin >> edges[0][i];//把0号节点视作源,从源到某个点代表猪圈的猪数
    //接下来输入n个顾客,分别用n个节点来表征他们。汇用第1102位表示。
    int LastCustomerOfFarm[MAXM];
    memset(LastCustomerOfFarm,-1,sizeof(LastCustomerOfFarm));

    int a;
    for (int i=0;i<n;++i) {//第i个顾客
        cin >> a;
        for (int j=0;j<a;++j) {
            int s;
            cin >> s;
            if (LastCustomerOfFarm[s] == -1) {
                LastCustomerOfFarm[s] = 1001+i;
                edges[s][1001+i] = INT_MAX;
            }
            else {
                edges[LastCustomerOfFarm[s]][1001+i] = INT_MAX;
                LastCustomerOfFarm[s] = 1001+i;
            }
        }
        int need;
        cin >> need;
        edges[1001+i][1102] = need;
    }
    cout << EK();
    return 0;
}

总结

这道题很巧妙地应用了最大流进行建模。首先,每个猪圈可以看作是从源处收到一定流量的节点,把顾客看作是通向汇的节点。最关键的是,如何将顾客和猪圈连接起来。由题意,每位顾客都拥有分配猪圈的权利,这就可以抽象为,每位顾客可以自己重建一个猪圈,这个猪圈能够向汇点输出,也能够为后续的顾客提供一个广阔的猪圈以供选择(最理想的情况是,最终所有猪圈都合为一体)。
以样例输入为例,构造出如图所示的网络:样例输入构造的网络
可以看到,虽然customer3只能接入猪圈2,但是它实际上是可以通过customer1的操作,拿到原本属于猪圈1的猪。但是可以看到,customer3无法拿到属于猪圈3的猪,因为customer2尽管和猪圈1相连,但是没有和猪圈2相连,所以customer2无法将猪圈3的猪运到猪圈2(这其实也是一个时间先后的问题,customer1的操作在customer2之前)。
BTW,我的解法时间较长,可能还存在更优的算法,之后再和同学交流一下。

声明

题目来自校内OJ平台,本人没有题目的版权。如有侵权,请联系本人删除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值