uva 753 A Plug for UNIX
最大流 OR 二分图匹配
题意:输入n,有n个插座,下面n行是每个插座的类型(最后24个字母来表示一个插座,没有空格放心用scanf,但是有可能插座会相同,但是这个没有什么影响)
输入m,有m个电器,下面m行每行两个单词分别是电器的名字和插头类型(同样24个字母单词内没空格,两个单词空格隔开)
输入k,有k个转换器,下面k行每行两个单词,分别表示转换器的入口类型和插头类型
每种转换器的个数是无限的,转换器本身可以与转换器相连
要你求,让最多的电器能够插在插座上(可以用转换器辅助也可以直接插上去),输入不能插上去的电器的数量
思路一:转化为最大流,重点还是如何建图,我是用邻接表建图的这样效率也高一点,其实用邻接矩阵建图也可以。我的建图方法是,电器从1到m标号,转换器从m+1到m+k标号,插座从m+k+1到m+k+n标号,另外设置一个源点s=0,汇点t=m+k+n+1 , 求s到t的最大流
s与所有电器建一条有向边,容量为1,所有插座与汇点建一条有向边,容量为1。对于每个电器,如果能直接和插座相连的,和每个能相连的插座建一条有向边,容量为1.另外,所有电器和所有能连接上的转换器建一条有向边,容量为INF,转换器和转换器之间能相连的建一条有向边容量为INF,转换器和插座能相连建一条有向边容量为INF。
建图完毕,直接EK最大流模板上去即可
注意几点:
1.邻接表建有向边不要忘记每条有向边附带一条反边,反边的容量为0
2.一定要记得建立电器和插座直接相连的边
3.一开始想复杂了,认为用电器和插座要拆点,转换器不用拆点,其实全部都不用拆点
4.不需要建无向图,另外建无向图应该是错的
#include <cstdio> #include <cstring> #include <queue> using namespace std; #define INF 0x3f3f3f3f #define MAXN 550 #define MAXM 50010 int N,M,K,CNT,edgenum,s,t,first[MAXN]; struct edge {int u,v,f,cap,next; }e[MAXM]; struct device //电器 {char s1[30],s2[30]; }d[MAXN]; struct adapter //转换器 {char s1[30], s2[30]; }a[MAXN]; struct receptacle //插座 {char s[30]; }r[MAXN]; void printfff() //测试打印函数 {printf("M=%d N=%d K=%d CNT=%d s=%d t=%d\n",M,N,K,CNT,s,t);for(int i=0; i<edgenum; i++)printf("%d: %d-->%d=%d %d\n",i,e[i].u,e[i].v,e[i].cap,e[i].next); } void add(int u ,int v , int cap) {int t;e[edgenum].u=u; e[edgenum].v=v;e[edgenum].f=0; e[edgenum].cap=cap;e[edgenum].next=first[u];first[u]=edgenum++;t=u; u=v; v=t;e[edgenum].u=u; e[edgenum].v=v;e[edgenum].f=0; e[edgenum].cap=0;e[edgenum].next=first[u];first[u]=edgenum++; } void input() {scanf("%d",&N);for(int i=1; i<=N; i++)scanf("%s",r[i].s);scanf("%d",&M);for(int i=1; i<=M; i++)scanf("%s%s",d[i].s1,d[i].s2);scanf("%d",&K);for(int i=1; i<=K; i++)scanf("%s%s",a[i].s1,a[i].s2); CNT=N+M+K; } void init() {memset(first,-1,sizeof(first));edgenum=0;s=0; t=CNT+1; //源点,汇点for(int i=1; i<=M; i++) //所有电器{add(s,i,1); //源点和电器建边容量为1for(int j=1; j<=K ; j++) //所有电器和转换器的连接if(!strcmp(d[i].s2 , a[j].s1)) //电器可以插入转换器中add(i,j+M,INF);for(int j=1; j<=N; j++) //电器和插座直接相连if(!strcmp(d[i].s2 , r[j].s))add(i,M+K+j,1);}for(int i=1; i<=K; i++) {for(int j=1; j<=K; j++) //所有转换器和转换器之间的连接if(i!=j && !strcmp(a[i].s2 , a[j].s1)) //不用考虑相同种类的转换器的连接,因为没有意义add(i+M,j+M,INF);for(int j=1; j<=N; j++) //所有转换器和所有插座连接if(!strcmp(a[i].s2 , r[j].s)) //转换器可插入插座中add(i+M , M+K+j , INF); //转化器和插座相连}for(int i=1; i<=N; i++) //插座和汇点相连add(M+K+i,t,1); } void EK() {queue<int>q;int a[MAXN],path[MAXN];int F;F=0;while(1){memset(path,-1,sizeof(path));memset(a,0,sizeof(a));a[s]=INF;q.push(s);while(!q.empty()){int u=q.front(); q.pop();for(int k=first[u]; k!=-1; k=e[k].next){int v=e[k].v;if(!a[v] && e[k].f<e[k].cap){a[v]=a[u]<e[k].cap-e[k].f?a[u]:e[k].cap-e[k].f;path[v]=k;q.push(v);}}}if(!a[t]) break;for(int k=path[t]; k!=-1; k=path[e[k].u]) //增广{//printf("%d<--%d ",e[k].v,e[k].u);e[k].f+=a[t];e[k^1].f-=a[t];}//printf("\n");F+=a[t];}//printf("F=%d\n",F);printf("%d\n",M-F); } int main() {int T;scanf("%d",&T);while(T--){input();init();//printfff();EK();if(T) printf("\n");}return 0; }
思路二:二分图最大匹配
其实用二分图的思想来看这个问题更加直观,即电器和插座匹配,(电器之间,插座之间不可能匹配)。问题同样是建图问题
用最大流建图的话,顶点会有转换器,但是二分图来建则没有(当然也不需要加额外的源点和汇点)。二分图建立的分两部分,一部分是,用电器能不能直接和某些插座直接相连,能的话直接建边。另外要利用转换器,看看能不能把不能直接相连电器和插座给连接起来,能的话就连接。我还没写,但是想到是用dfs去找,不知道效率如何。
最后建了二分图,不需要管那些边是怎么连接的(直接相连或者借助了转换器),直接把匈牙利丢上去即可
明天起来再写二分图了
二分图写好了,思路就是上面说的那样,嗯写二分图舒心多了,建边少代码也少一些。不过我是用dfs去判断电器能不能通过转换器连到插座上的,所以时间慢了点,0.060s,上面的最大流是0.008s
另外一个地方是。电器不能直接与插座相连,然后用dfs去找能不能通过转换器去相连,虽然每种转换器的数量是无限的,但是在一趟寻找中一种转换器没必要被用两次(其实那种入口和插头相同的转换器也没必要用上,但是不做特殊判断了,没影响),也就是可以看成路径,虽然有环,但是这个环显然是没必要经过,所以加了一个vis数组标记
#include <cstdio> #include <cstring> #include <queue> using namespace std; #define INF 0x3f3f3f3f #define MAXN 250 #define MAXM 40100 int N,M,K,CNT,edgenum,first[MAXN],mat[MAXN],vis[MAXN],cov[MAXN]; struct edge {int u,v,next; }e[MAXM]; struct device //电器 {char s1[30],s2[30]; }d[MAXN]; struct adapter //转换器 {char s1[30], s2[30]; }a[MAXN]; struct receptacle //插座 {char s[30]; }r[MAXN]; void add(int u , int v) {e[edgenum].u=u; e[edgenum].v=v;e[edgenum].next=first[u];first[u]=edgenum++; } int dfs(int j , int k) //目标是j插座,当前是k转换器 {if(!strcmp(a[k].s2 , r[j].s)) //当前转换器能插入插座中return 1;vis[k]=1; //标记k转换器被用过,在一趟dfs中一种转换器没必要被用两次for(int i=1; i<=K; i++) if(!vis[i]) //所有没被用过的转换器if(!strcmp(a[k].s2 , a[i].s1)) {//另外k转换器可以插入i转换器中if(dfs(j,i))return 1;vis[i]=0;}return 0;} int find(int i , int j) //i电器和j插座 {memset(vis,0,sizeof(vis));for(int k=1; k<=K; k++) //所有转换器if(!strcmp(d[i].s2 , a[k].s1)) {//电器能插入转换器的入口if(dfs(j,k))return 1;vis[k]=0;}return 0; } void input() {scanf("%d",&N);for(int i=1; i<=N; i++)scanf("%s",r[i].s);scanf("%d",&M);for(int i=1; i<=M; i++)scanf("%s%s",d[i].s1,d[i].s2);scanf("%d",&K);for(int i=1; i<=K; i++)scanf("%s%s",a[i].s1,a[i].s2); CNT=N+M+K; } void init() {memset(first,-1,sizeof(first));edgenum=0;for(int i=1; i<=M; i++) //所有电器 for(int j=1; j<=N; j++) //所有插座 {if(!strcmp(d[i].s2 , r[j].s)) //可以直接相连 {//printf("直接相连%d---%d\n",i,j);add(i,j+M); //建立有向边 }else if(find(i,j)) //如果通过转换器能连接上 {//printf("通过转换器连接%d---%d\n",i,j);add(i,j+M);}} } void printfff() {for(int i=0; i<edgenum; i++)printf("%d %d %d\n",e[i].u,e[i].v,e[i].next); } int dfs_match(int u) {for(int k=first[u]; k!=-1; k=e[k].next){int v=e[k].v;if(!cov[v]){cov[v]=1;if( mat[v]==-1 || dfs_match(mat[v]) ){ mat[v]=u; return 1; }}}return 0; } void maxmatch() {int max=0;memset(mat,-1,sizeof(mat));for(int i=1; i<=M+N; i++) //所有的点都做一次起点 {memset(cov,0,sizeof(cov));max+=dfs_match(i);}//for(int i=1; i<=M+N; i++)//printf("%d<--->%d\n",i,mat[i]);//printf("max=%d\n",max);printf("%d\n",M-max); } int main() {int T;scanf("%d",&T);while(T--){input();init();//printfff(); maxmatch();if(T) printf("\n");}return 0; }
uva 753 A Plug for UNIX相关推荐
- UVA 753 A Plug for UNIX (最大流)
关键在建图,转换器连一条容量无限的边表示可以转化无数次,设备的插头连源点,插座连汇点. dinic手敲已熟练,输出格式又被坑,总结一下,输出空行多case的,一个换行是必要的,最后一个不加空行,有Te ...
- UVA - 753 A Plug for UNIX(网络流)
题意 给定一些插头设备和插座,有一些方法可以把其中一些插头变成另一种插头.求无法匹配插座的插头设备个数. 题解 用\(map\)给每个字符串标号为\(a_i\)和\(b_i\). 读入每种改变插头的方 ...
- POJ 1087 A Plug for UNIX 会议室插座问题 构图+最大流
题目链接:POJ 1087 A Plug for UNIX A Plug for UNIX Time Limit: 1000MS Memory Limit: 65536K Total Submis ...
- 解题报告 之 POJ1087 A Plug for UNIX
解题报告 之 POJ1087 A Plug for UNIX Description You are in charge of setting up the press room for the in ...
- POJ 1087 -- A Plug for UNIX(最大流,建图)(文末有极限数据)
题目链接 Description You are in charge of setting up the press room for the inaugural meeting of the Uni ...
- 【POJ - 1087】A Plug for UNIX(建图,网络流最大流)
题干: You are in charge of setting up the press room for the inaugural meeting of the United Nations I ...
- POJ - 1087 A Plug for UNIX(最大流)
题目链接:点击查看 题目大意:给出n个互不相同的设备,m个插座以及k种适配器,每种适配器都有无限个,适配器可以互相搭配,问如何匹配可以让尽可能多的设备用上电 题目分析:裸的最大流,就是加上了个字符串把 ...
- poj 1087 A Plug for UNIX 【最大流】
题目连接:http://poj.org/problem? id=1087 题意: n种插座 ,m个电器,f组(x,y)表示插座x能够替换插座y,问你最多能给几个电器充电. 解法:起点向插座建边,容量1 ...
- uva 753(网络流最大流)
网络流最大流问题,这里使s=0,使s与所有的插头相连,最大通量为1,然后插头和转换器相连,最大通量为1,转换器和转换器相连,因为有无限个,所以为inf,然后转换器和插座连,最大通量为1,插座和t相连, ...
最新文章
- Net平台下的分布式缓存设计
- tracert 路由跟踪程序
- 【HDU - 1757】A Simple Math Problem (矩阵快速幂)
- 协程asyncio_Asyncio深入浅出
- 01-02 Linux常用命令-文件处理
- 主机硬件系统主板状态 vmware_电脑主机启动不了怎么办?
- 隐藏十年的 Sudo 漏洞 (CVE-2021-3156) 还影响 macOS 和 IBM IAX
- C语言函数库之字符串拷贝函数(string.h)
- xci转nsp工具_【ns新系统11.0.0发布】安装工具已经更新至4.2【后面附上批处理内容修改】...
- 基于minio及tus断点续传及断点下载解决方案
- 语音机器人在人工智能领域的发展
- Notepad++ 7.6版本 安装hexeditor最新详细版本(小白版)
- 麒麟安全IPO过会:拟募资6.6亿 第一季营收下降40%
- IDEA新建项目需要新建好文件夹
- 如何组建和管理测试团队
- 找外包公司做小程序都有哪些坑?
- freemind 要下载java_Freemind
- 身份证拍照识别软件SDK
- 如何去处右下角任务栏闪动邮件图标小广告?WPS干的好事!
- 有手就行, RAW格式批量转JPG