A Plug for UNIX 最大流dinic

题目:

You are in charge of setting up the press room for the inaugural meeting of the United Nations Internet eXecutive (UNIX), which has an international mandate to make the free flow of information and ideas on the Internet as cumbersome and bureaucratic as possible. 
Since the room was designed to accommodate reporters and journalists from around the world, it is equipped with electrical receptacles to suit the different shapes of plugs and voltages used by appliances in all of the countries that existed when the room was built. Unfortunately, the room was built many years ago when reporters used very few electric and electronic devices and is equipped with only one receptacle of each type. These days, like everyone else, reporters require many such devices to do their jobs: laptops, cell phones, tape recorders, pagers, coffee pots, microwave ovens, blow dryers, curling 
irons, tooth brushes, etc. Naturally, many of these devices can operate on batteries, but since the meeting is likely to be long and tedious, you want to be able to plug in as many as you can. 
Before the meeting begins, you gather up all the devices that the reporters would like to use, and attempt to set them up. You notice that some of the devices use plugs for which there is no receptacle. You wonder if these devices are from countries that didn't exist when the room was built. For some receptacles, there are several devices that use the corresponding plug. For other receptacles, there are no devices that use the corresponding plug. 
In order to try to solve the problem you visit a nearby parts supply store. The store sells adapters that allow one type of plug to be used in a different type of outlet. Moreover, adapters are allowed to be plugged into other adapters. The store does not have adapters for all possible combinations of plugs and receptacles, but there is essentially an unlimited supply of the ones they do have.

输入:

The input will consist of one case. The first line contains a single positive integer n (1 <= n <= 100) indicating the number of receptacles in the room. The next n lines list the receptacle types found in the room. Each receptacle type consists of a string of at most 24 alphanumeric characters. The next line contains a single positive integer m (1 <= m <= 100) indicating the number of devices you would like to plug in. Each of the next m lines lists the name of a device followed by the type of plug it uses (which is identical to the type of receptacle it requires). A device name is a string of at most 24 alphanumeric 
characters. No two devices will have exactly the same name. The plug type is separated from the device name by a space. The next line contains a single positive integer k (1 <= k <= 100) indicating the number of different varieties of adapters that are available. Each of the next k lines describes a variety of adapter, giving the type of receptacle provided by the adapter, followed by a space, followed by the type of plug.

输出:

A line containing a single non-negative integer indicating the smallest number of devices that cannot be plugged in.

样例输入:

4 
A 
B 
C 
D 
5 
laptop B 
phone C 
pager B 
clock B 
comb X 
3 
B X 
X A 
X D 

样例输出:

1

题意:

在一个房间里有n种类型的插座,有m种要插入的设备,有k种适配器。求不能插入设备的最小数量。

首先输入n行房间里已经存在的插座类型,再输入m个要插入的设备名称,后面跟着它们使用的插头类型(与他需要的插座类型相同),最后输入k种适配器,给出适配器提供的插座类型以及他的插头类型(比如说 A B 就是把B变成A)

下面给出一张图帮助理解(案例)

S是超级源点,E是超级汇点,开始用map把房间里已经存在的插座类型储存起来(便于把这些已经存在的类型与设备一一连接), 由于本来存在的这些插座类型只能使用一次,所以建图的时候源点与他们之间的权值为1,因为每个设备只有一个,所以设备和他匹配的类型之间的权值也为1,。(注:反向弧都为0)

提醒自己,这样的建图方式决定了add要成双成对,别忘了反向弧的存在;入队别忘了出队;

AC代码;

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
#include <string>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn=100100;
int head[maxn],dis[maxn],cur[maxn];
int cnt;
const int INF=0x7fffffff;
int S,E,n,m,k;
map<string,int>hap;
struct edge
{
    int u,v,c,next;
}node[4*maxn];
void add(int u,int v,int c)
{
    node[cnt].u=u;
    node[cnt].v=v;
    node[cnt].c=c;
    node[cnt].next=head[u];
    head[u]=cnt++;
}
bool bfs()
{
    memset(dis,0,sizeof(dis));
    queue<int>q;
    q.push(S);
    dis[S]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u];i!=-1;i=node[i].next)
        {
            edge e=node[i];
            if(!dis[e.v]&&e.c>0)
            {
                dis[e.v]=dis[e.u]+1;
                q.push(e.v);
                if(e.v==E) return 1;
            }
        }
    }
    return dis[E]!=0;
}
int dfs(int u,int mx)
{
    int ret=0;
    if(u==E||mx==0) return mx;
    for(int &i=cur[u];i!=-1;i=node[i].next)
    {
        edge e=node[i];
        if(dis[e.v]==dis[e.u]+1&&e.c>0)
        {
            int w=dfs(e.v,min(mx,e.c));
            node[i].c-=w;
            node[i^1].c+=w;
            ret+=w;
            mx-=w;
            if(mx==0)break;
        }
    }
    return ret;
}
int dinic()
{
    int maxflow=0;
    while(bfs())
    {
        memcpy(cur,head,sizeof(head));
        maxflow+=dfs(S,INF);
    }
    return maxflow;
}
int main()
{
        scanf("%d",&n);
        cnt=0;
        memset(head,-1,sizeof(head));
        S=0,E=n+3*m+2*k+1;//超级汇点可以不用非得是这样,只要定义的点以前没出现过就好了

        for(int i=1;i<=n;i++)
        {
            string str1;
            cin>>str1;
            hap[str1]=i;//把已经存在的插座类型用map储存
            add(S,i,1);//超级源点与已经存在的插座类型连接
            add(i,S,0);
        }
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            string str2,str3;
            cin>>str2>>str3;
            add(n+i,n+m+i,1);//设备与各自的匹配类型连接
            add(n+m+i,n+i,0);
            add(n+m+i,E,INF);//设备所匹配的类型与超级汇点连接
            add(E,n+m+i,0);
            if(!hap[str3])//如果设备匹配的这些类型中开始不存在的话,就给他定义一个点
            {
                hap[str3]=n+2*m+i;
            }
            add(hap[str3],n+i,INF);//将已经存在的插座类型与他匹配的设备连接
            add(n+i,hap[str3],0);
        }
        scanf("%d",&k);
        for(int i=1;i<=k;i++)
        {
            string str4,str5;
            cin>>str4>>str5;
            if(!hap[str4])hap[str4]=n+3*m+i;//这两句与上面的if意思一样
            if(!hap[str5])hap[str5]=n+3*m+k+i;
            add(hap[str5],hap[str4],INF);//将这些适配器连接起来
            add(hap[str4],hap[str5],0);
        }
        printf("%d\n",m-dinic());//因为要输出不能插入的设备的最小数量,而我们求的是能插入的设备的数量,所以要用设备总数量m减去能插入的,剩下的就是不能插入的

    return 0;
}

 

【Koopman】遍历论、动态模态分解和库普曼算子谱特性的计算研究(Matlab代码实现)内容概要:本文围绕【Koopman】遍历论、动态模态分解和库普曼算子谱特性的计算研究展开,重点介绍基于Matlab的代码实现方法。文章系统阐述了遍历理论的基本概念、动态模态分解(DMD)的数学原理及其与库普曼算子谱特性之间的内在联系,展示了如何通过数值计算手段分析非线性动力系统的演化行为。文中提供了完整的Matlab代码示例,涵盖数据驱动的模态分解、谱分析及可视化过程,帮助读者理解并复现相关算法。同时,文档还列举了多个相关的科研方向和技术应用场景,体现出该方法在复杂系统建模与分析中的广泛适用性。; 适合人群:具备一定动力系统、线性代数与数值分析基础,熟悉Matlab编程,从事控制理论、流体力学、信号处理或数据驱动建模等领域研究的研究生、博士生及科研人员。; 使用场景及目标:①深入理解库普曼算子理论及其在非线性系统分析中的应用;②掌握动态模态分解(DMD)算法的实现与优化;③应用于流体动力学、气候建模、生物系统、电力系统等领域的时空模态提取与预测;④支撑高水平论文复现与科研项目开发。; 阅读建议:建议读者结合Matlab代码逐段调试运行,对照理论推导加深理解;推荐参考文中提及的相关研究方向拓展应用场景;鼓励在实际数据上验证算法性能,并尝试改进与扩展算法功能。
最大流Dinic算法是一种用于解决网络最大流问题的高效算法。在最大流问题里,图被称为“网络”,每条边的边权称作“流量”,有一个起点(源点)和一个终点(汇点)。最大流是指网络中满足弧流量限制条件和平衡条件且具有最大流量的可行流,可形象理解为源点有无限吨水的水库,汇点希望得到最多的水,每个河道有输水限制,求解汇点最多能得到几吨水 [^2]。 Dinic算法的核心基于BFS来构建分层图,在分层图中,两点之间的边只能从低层指向高层。若一条边连接的两个点的层次差为1,则该边称为增广路径(即从源点到汇点的路径,且路径中的每条边都有剩余容量)。每次找到一条增广路径,就把该路径上所有边的剩余容量减去该路径上容量的最小值,并且将该路径上所有边的反边加入到分层图中,直至没有增广路径为止,此时可得到最大流 [^4]。 该算法的复杂度方面,最多仅需 n - 1 轮增广即可求得最大流,单轮增广的最坏复杂度是 O(nm),总的复杂度为 O(n²m),不过对于随机图,Dinic的实际运行速度要远优于这一时间上界 [^1]。 以下是一段简单的Dinic算法的代码示例(使用Python): ```python from collections import deque INF = float('inf') class Edge: def __init__(self, to, cap, rev): self.to = to self.cap = cap self.rev = rev # 构建图 def add_edge(graph, from_, to, cap): graph[from_].append(Edge(to, cap, len(graph[to]))) graph[to].append(Edge(from_, 0, len(graph[from_]) - 1)) # BFS分层 def bfs(graph, level, s): n = len(graph) level[:] = [-1] * n level[s] = 0 que = deque([s]) while que: v = que.popleft() for edge in graph[v]: if edge.cap > 0 and level[edge.to] < 0: level[edge.to] = level[v] + 1 que.append(edge.to) # DFS找增广路径 def dfs(graph, level, iter_, v, t, f): if v == t: return f while iter_[v] < len(graph[v]): edge = graph[v][iter_[v]] if edge.cap > 0 and level[v] < level[edge.to]: d = dfs(graph, level, iter_, edge.to, t, min(f, edge.cap)) if d > 0: edge.cap -= d graph[edge.to][edge.rev].cap += d return d iter_[v] += 1 return 0 # Dinic主函数 def Dinic(graph, s, t): n = len(graph) max_flow = 0 while True: level = [-1] * n bfs(graph, level, s) if level[t] < 0: return max_flow iter_ = [0] * n while True: flow = dfs(graph, level, iter_, s, t, INF) if flow == 0: break max_flow += flow # 示例使用 n = 4 graph = [[] for _ in range(n)] add_edge(graph, 0, 1, 2) add_edge(graph, 0, 2, 3) add_edge(graph, 1, 2, 1) add_edge(graph, 1, 3, 3) add_edge(graph, 2, 3, 2) s = 0 t = 3 max_flow = Dinic(graph, s, t) print("最大流:", max_flow) ``` 最大流Dinic算法在很多领域有应用,例如在交通网络中,可以将道路看作边,车流量限制看作边的容量,源点和汇点可以是城市中的两个地点,通过该算法可以计算出两个地点之间的最大车流量;在通信网络中,可将通信线路看作边,带宽看作边的容量,计算两点之间的最大数据传输量等。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值