网络流 最大流+拆点+路径输出

本文通过网络流模型解决了一个关于电脑公司如何最大化单位时间产量的问题。具体地,通过建立一个包括原点S、汇点T和各个机器节点的网络,利用Dinic算法求解最大流,并进一步确定了哪些机器之间存在协作关系。

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

题目链接:http://poj.org/problem?id=3436
题目大意:
电脑公司生产电脑有N个机器,每个机器单位时间产量为Qi。 电脑由P个部件组成,每个机器工作时只能把有某些部件的半成品电脑(或什么都没有的空电脑)变成有另一些部件的半成品电脑或完整电脑(也可能移除某些部件)。求电脑公司的单位时间最大产量,以及哪些机器有协作关系,即一台机器把它的产品交给哪些机器加工。

Sample input

3 4

15 0 0 0 0 1 0

10 0 0 0 0 1 1

30 0 1 2 1 1 1
3 0 2 1 1 1 1

Sample output

25 2

1 3 15

2 3 10

输入:电脑由3个部件组成,共有4台机器,1号机器产量15, 能给空电脑加上2号部件,2号 机器能给空电脑加上2号部件和3号部件, 3号机器能把有1个2号部件和3号部件有无均可的电脑变成成品(每种部件各有一个)
输出:单位时间最大产量25,有两台机器有协作关系,
1号机器单位时间内要将15个电脑给3号机器加工
2号机器单位时间内要将10个电脑给3号机器加工

思路:

网络流模型:

  1. 添加一个原点S,S提供最初的原料 00000…
  2. 添加一个汇点T, T接受最终的产品 11111…
  3. 将每个机器拆成两个点: 编号为i的接收节点,和编号为i+n的产出节点(n是机器数目),前者用于接收原料,后者用于提供加工后的半成品或成品。这两个点之间要连一条边,容量为单位时间产量Qi
  4. S 连边到所有接收 “0000…” 或 “若干个0及若干个2” 的机器,容量为无穷大
  5. 产出节点连边到能接受其产品的接收节点,容量无穷大
  6. 能产出成品的节点,连边到T,容量无穷大。
  7. 求S到T的最大流
    在这里插入图片描述
    坑点:s可以与只包含0和2的连接。

路径输出:残流网络反向边。

#include<bits/stdc++.h>
using namespace std;

int s[2][55][15];
int L[55];

const int maxn=1e5+10;
const int maxm=2e5+10;

struct E
{
    int v;   //每一条边指向的点
    int next;//指向对应点的前一条边
    int w;   //每一条边的残量

}e[maxm];

int ss, tt;//源点和汇点
int cut;//边的数量,从0开始编号
int head[maxm];//每一个点最后一条边的编号
int d[maxn];//分层图中标记深度
int inf=(1<<31)-1;
int cur[maxn];//cur就是记录当前点u循环到了哪一条边
int n, m;

void init()
{
    cut=-1;
    memset(head, -1, sizeof(head));
}

void addEdge(int u, int v, int w)
{
    cut++;
    e[cut].next=head[u];
    e[cut].v=v;
    e[cut].w=w;
    head[u]=cut;
}

void add(int u, int v, int w)
{
    addEdge(u, v, w);
    addEdge(v, u, 0);
}

int bfs()
{
    queue<int> q;
    while(!q.empty())
    {
        q.pop();
    }
    memset(d, 0, sizeof(d));
    d[ss]=1;
    q.push(ss);
    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, w=e[i].w;
            if(w>0&&d[v]==0)
            {
                d[v]=d[u]+1;
                q.push(v);
            }
        }
    }
    if(d[tt]==0)
    {
        return 0;
    }

    return 1;
}

int dfs(int u, int dis)
{
    if(u==tt)
    {
        return dis;
    }

    for(int &i=cur[u];i!=-1;i=e[i].next)
    {
        int v=e[i].v, w=e[i].w;
        if((d[v]==d[u]+1)&&w!=0)
        {
            int di=dfs(v, min(dis, w));
            if(di>0)
            {
                e[i].w-=di;
                e[i^1].w+=di;
                return di;
            }
        }
    }

    return 0;
}

int Dinic()
{
    int ans=0;
    while (bfs())
    {
        for(int i=ss;i<=tt;i++)
        {
            cur[i]=head[i];
        }
        while (int d=dfs(ss,inf))
        {
            ans+=d;
        }
    }
    return ans;
}

int main()
{
    int p, n;
    while(scanf("%d%d",&p,&n)!=EOF)
    {
        init();
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&L[i]);
            for(int j=0;j<p;j++)
            {
                scanf("%d",&s[0][i][j]);
            }
            for(int j=0;j<p;j++)
            {
                scanf("%d",&s[1][i][j]);
            }
        }
        for(int i=1;i<=n;i++)
        {
            add(i, i+n, L[i]);
            for(int j=1;j<=n;j++)
            {
                if(i==j)
                {
                    continue;
                }
                else
                {
                    int q=0;
                    for(int k=0;k<p;k++)
                    {
                        if(s[0][j][k]!=2)
                        {
                            if(s[1][i][k]!=s[0][j][k])
                            {
                                q=1;
                                break;
                            }
                        }
                    }
                    if(q==0)
                    {
                        add(i+n, j, inf);
                    }
                }
            }
        }
        for(int i=1;i<=n;i++)
        {
            int q=0;
            for(int j=0;j<p;j++)
            {
                if(s[0][i][j]==1)
                {
                    q=1;
                    break;
                }
            }
            if(q==0)
            {
                add(0, i, inf);
            }
            q=0;
            for(int j=0;j<p;j++)
            {
                if(s[1][i][j]!=1)
                {
                    q=1;
                    break;
                }
            }
            if(q==0)
            {
                add(i+n, 2*n+1, inf);
            }
        }
        ss=0, tt=2*n+1;
        cout<<Dinic()<<" ";

        int ans=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=head[i];j!=-1;j=e[j].next)
            {
                if(e[j].w!=0&&e[j].v!=0&&e[j].v!=i+n)
                {
                    ans++;
                }
            }
        }
        cout<<ans<<endl;
        for(int i=1;i<=n;i++)
        {
            for(int j=head[i];j!=-1;j=e[j].next)
            {
                if(e[j].w!=0&&e[j].v!=0&&e[j].v!=i+n)
                {
                    printf("%d %d %d\n",e[j].v-n,i, e[j].w);
                }
            }
        }
    }

    return 0;
}

/*
1 3
10 0 1
10 2 1
20 1 0
*/

### 最小割集的计算方法 对于题目描述的情况,可以将其建模为一个网络流问题,并通过最小割来解决。以下是具体的分析和实现。 #### 1. 建模过程 为了找到使 A 和 B 不再相连所需的最少删除人数,可以通过构建一个二分图模型来进行处理。每个人的节分为两个部分:入 \( u_{in} \) 和出 \( u_{out} \),并通过一条容量为 1 的边连接它们。如果两个人之间存在联系,则在这两个人对应的出和另一个的入间建立一条容量无限大的边[^3]。 这种建模方式使得当某个节被移除时(即切断其内部的容量为 1 的边),该人的所有关联都会断开。而那些表示人际关系的无限大容量边则确保只有在实际删除对应的人之后才会破坏这些关系。 #### 2. 使用最大流算法求解最小割 在网络流理论中,著名的 **最大流等于最小割定理** 提供了一种有效的方法来解决问题。因此,在上述构建成的图上运行任意一种有效的最大流算法即可得到答。常用的算法包括但不限于 Ford-Fulkerson 方法以及更高效的 Dinic 或者 Push-Relabel 算法。 一旦获得了最大流量值,这个数值就代表了从源到汇之间的最小割大小——也就是我们需要删去的最少数目的人员数量以阻止 A 和 B 的通信路径[^4]。 #### 3. 字典序约束下的优化策略 由于本题还额外要求输出字典序最小的结果之一,所以在单纯依靠最大流/最小割的基础上还需要进一步考虑如何选取具体哪些顶作为最终结果: - 可以先尝试按照编号从小到达顺序依次枚举每一个可能成为候选者的个体; - 对于每一轮试验性的选择操作后重新评估当前状态下是否存在其他可行解满足条件的同时保持总成本不变但具有更低的整体序列号排列特性; - 继续迭代直至遍历完成全部可能性空间为止. 这种方法虽然理论上增加了时间复杂度,但在实践中往往因为剪枝等原因表现良好. ```python from collections import defaultdict class Edge: def __init__(self, from_, to, capacity): self.from_ = from_ self.to = to self.capacity = capacity self.flow = 0 def add_edge(graph, v_from, v_to, cap): forward_edge = Edge(v_from, v_to, cap) backward_edge = Edge(v_to, v_from, 0) graph[v_from].append(forward_edge) graph[v_to].append(backward_edge) def bfs(graph, parent, source, sink): visited = set() queue = [(source)] while queue: current_node = queue.pop(0) if current_node not in visited: visited.add(current_node) for edge in graph[current_node]: residual_capacity = edge.capacity - edge.flow if residual_capacity > 0 and edge.to not in visited: parent[edge.to] = (current_node, edge) if edge.to == sink: return True queue.append(edge.to) return False def edmonds_karp(graph, source, sink): max_flow = 0 n = len(graph) parent = [-1]*n while bfs(graph, parent, source, sink): path_flow = float('Inf') s = sink while(s != source): prev_node, current_edge = parent[s] path_flow = min(path_flow, current_edge.capacity - current_edge.flow) s = prev_node max_flow += path_flow v = sink while(v != source): prev_node, current_edge = parent[v] current_edge.flow += path_flow reverse_edge_found = next((e for e in graph[current_edge.to] if e.to==prev_node), None) reverse_edge_found.flow -= path_flow v = prev_node return max_flow # Example Usage: if __name__ == "__main__": N = ... # Number of people including A & B. M = ... # Number of relationships between pairs of individuals. edges = [...] # List containing tuples representing connections among nodes like so: [(u,v)] where 'u' connects with 'v'. G = [[]for _ in range(N*2+2)] S,T=N,N+1 # Add capacities according to problem statement rules here... result=edmonds_karp(G,S,T) print(result) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值