UVa 753 - A Plug for UNIX(最大流)
链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=694
题意:
有n个插座,m个设备和k(n,m,k≤100)种转换器,每种转换器都有无限多。已知每个插座的类型,每个设备的插头类型,
以及每种转换器的插座类型和插头类型。插头和插座类型都用不超过24个字母表示,插头只能插到类型名称相同的插座中。
例如,有4个插座,类型分别为A, B, C, D;有5个设备,插头类型分别为B, C, B, B, X;
还有3种转换器,分别是B->X,X->A和X->D。这里用B->X表示插座类型为B,插头类型为X,
因此一个插头类型为B的设备插上这种转换器之后就“变成”了一个插头类型为X的设备。
转换器可以级联使用,例如插头类型为A的设备依次接上A->B,B->C,C->D这3个转换器之后会“变成”插头类型为D的设备。
要求插的设备尽量多。问最少剩几个不匹配的设备。
分析:
首先要注意的是:k个转换器中涉及的插头类型不一定是接线板或者设备中出现过的插头类型。
在最坏情况下,100个设备,100个插座,100个转换器最多会出现400种插头。
当然,400种插头的情况肯定是无解的,但是如果编码不当,这样的情况可能会让程序出现下标越界等运行错误。
转换器有无限多,所以可以独立计算出每个设备i是否可以接上0个或多个转换器之后插到第j个插座上,
方法是建立有向图G,结点表示插头类型,边表示转换器,然后使用Floyd算法,
计算出任意一种插头类型a是否能转化为另一种插头类型b。
接下来构造网络:设设备i对应的插头类型编号为device[i],插座i对应的插头类型编号为recep[i],
则源点s到所有device[i]连一条弧,容量为1,然后所有recep[i]到汇点t连一条弧,容量为1,
对于所有设备i和插座j,如果device[i]可以转化为recep[j],则从device[i]连一条弧到recep[j],容量为1,
最后求s-t最大流,答案就是m减去最大流量。
代码:
//Dinic版
#include <cstdio>
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <queue>
using namespace std; /// 结点下标从0开始,注意maxn
struct Dinic {
static const int maxn = 1e3 + ;
static const int INF = 0x3f3f3f3f;
struct Edge {
int from, to, cap, flow;
}; int n, m, s, t; // 结点数,边数(包括反向弧),源点编号和汇点编号
vector<Edge> edges; // 边表。edges[e]和edges[e^1]互为反向弧
vector<int> G[maxn]; // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
bool vis[maxn]; // BFS使用
int d[maxn]; // 从起点到i的距离
int cur[maxn]; // 当前弧下标 void init(int n) {
this->n = n;
edges.clear();
for(int i = ; i < n; i++) G[i].clear();
}
void AddEdge(int from, int to, int cap) {
edges.push_back((Edge){from, to, cap, });
edges.push_back((Edge){to, from, , });
m = edges.size();
G[from].push_back(m-);
G[to].push_back(m-);
}
bool BFS() {
memset(vis, , sizeof(vis));
queue<int> Q;
Q.push(s);
vis[s] = ;
d[s] = ;
while(!Q.empty()) {
int x = Q.front(); Q.pop();
for(int i = ; i < G[x].size(); i++) {
Edge& e = edges[G[x][i]];
if(!vis[e.to] && e.cap > e.flow) { // 只考虑残量网络中的弧
vis[e.to] = ;
d[e.to] = d[x] + ;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x, int a) {
if(x == t || a == ) return a;
int flow = , f;
for(int& i = cur[x]; i < G[x].size(); i++) { // 从上次考虑的弧
Edge& e = edges[G[x][i]];
if(d[x]+ == d[e.to] && (f=DFS(e.to, min(a, e.cap-e.flow))) > ) {
e.flow += f;
edges[G[x][i]^].flow -= f;
flow += f;
a -= f;
if(a == ) break;
}
}
return flow;
}
int Maxflow(int s, int t) {
this->s = s; this->t = t;
int flow = ;
while(BFS()) {
memset(cur, , sizeof(cur));
flow += DFS(s, INF);
}
return flow;
}
vector<int> Mincut() { // 在Maxflow之后调用
vector<int> ans;
for(int i = ; i < edges.size(); i++) {
Edge& e = edges[i];
if(vis[e.from] && !vis[e.to] && e.cap > ) ans.push_back(i);
}
return ans;
}
}; const int UP = + ;
int recep[UP], device[UP];
bool can[UP*][UP*];
map<string,int> ids;
Dinic dc; int id(string s) {
if(ids.count(s)) return ids[s];
int i = ids.size();
return ids[s] = i;
} void floyd() {
int n = ids.size();
for(int i = ; i < n; i++) can[i][i] = true;
for(int k = ; k < n; k++) {
for(int i = ; i < n; i++) {
for(int j = ; j < n; j++) {
can[i][j] |= can[i][k] && can[k][j];
}
}
}
} int main() {
int T, n, m, k;
char s[+], s2[+];
scanf("%d", &T);
while(T--) {
ids.clear();
memset(can, false, sizeof(can));
scanf("%d", &n);
for(int i = ; i < n; i++) {
scanf("%s", s);
recep[i] = id(s);
}
scanf("%d", &m);
for(int i = ; i < m; i++) {
scanf("%s%s", s2, s);
device[i] = id(s);
}
scanf("%d", &k);
for(int i = ; i < k; i++) {
scanf("%s%s", s, s2);
can[id(s)][id(s2)] = true;
} floyd();
int V = ids.size();
dc.init(V+);
for(int i = ; i < m; i++) dc.AddEdge(V, device[i], );
for(int i = ; i < n; i++) dc.AddEdge(recep[i], V+, );
for(int i = ; i < m; i++) {
for(int t = ; t < n; t++) {
if(!can[device[i]][recep[t]]) continue;
dc.AddEdge(device[i], recep[t], );
}
}
printf("%d\n", m - dc.Maxflow(V, V+));
if(T) printf("\n");
}
return ;
}
//ISAP版
#include <cstdio>
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <queue>
using namespace std; /// 结点下标从0开始,注意maxn
struct ISAP {
static const int maxn = 1e3 + ;
static const int INF = 0x3f3f3f3f;
struct Edge {
int from, to, cap, flow;
}; int n, m, s, t; // 结点数,边数(包括反向弧),源点编号和汇点编号
vector<Edge> edges; // 边表。edges[e]和edges[e^1]互为反向弧
vector<int> G[maxn]; // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
bool vis[maxn]; // BFS使用
int d[maxn]; // 从起点到i的距离
int cur[maxn]; // 当前弧下标
int p[maxn]; // 可增广路上的上一条弧
int num[maxn]; // 距离标号计数 void init(int n) {
this->n = n;
edges.clear();
for(int i = ; i < n; i++) G[i].clear();
}
void AddEdge(int from, int to, int cap) {
edges.push_back((Edge){from, to, cap, });
edges.push_back((Edge){to, from, , });
m = edges.size();
G[from].push_back(m-);
G[to].push_back(m-);
}
bool BFS() {
memset(vis, , sizeof(vis));
queue<int> Q;
Q.push(t);
vis[t] = ;
d[t] = ;
while(!Q.empty()) {
int x = Q.front(); Q.pop();
for(int i = ; i < G[x].size(); i++) {
Edge& e = edges[G[x][i]^];
if(!vis[e.from] && e.cap > e.flow) {
vis[e.from] = ;
d[e.from] = d[x] + ;
Q.push(e.from);
}
}
}
return vis[s];
}
int Augment() {
int x = t, a = INF;
while(x != s) {
Edge& e = edges[p[x]];
a = min(a, e.cap-e.flow);
x = edges[p[x]].from;
}
x = t;
while(x != s) {
edges[p[x]].flow += a;
edges[p[x]^].flow -= a;
x = edges[p[x]].from;
}
return a;
}
int Maxflow(int s, int t) {
this->s = s; this->t = t;
int flow = ;
BFS();
memset(num, , sizeof(num));
for(int i = ; i < n; i++) num[d[i]]++;
int x = s;
memset(cur, , sizeof(cur));
while(d[s] < n) {
if(x == t) {
flow += Augment();
x = s;
}
int ok = ;
for(int i = cur[x]; i < G[x].size(); i++) {
Edge& e = edges[G[x][i]];
if(e.cap > e.flow && d[x] == d[e.to] + ) { // Advance
ok = ;
p[e.to] = G[x][i];
cur[x] = i; // 注意
x = e.to;
break;
}
}
if(!ok) { // Retreat
int m = n-; // 初值注意
for(int i = ; i < G[x].size(); i++) {
Edge& e = edges[G[x][i]];
if(e.cap > e.flow) m = min(m, d[e.to]);
}
if(--num[d[x]] == ) break; // gap优化
num[d[x] = m+]++;
cur[x] = ; // 注意
if(x != s) x = edges[p[x]].from;
}
}
return flow;
}
vector<int> Mincut() { // 在Maxflow之后调用
BFS();
vector<int> ans;
for(int i = ; i < edges.size(); i++) {
Edge& e = edges[i];
if(!vis[e.from] && vis[e.to] && e.cap > ) ans.push_back(i);
}
return ans;
}
}; const int UP = + ;
int recep[UP], device[UP];
bool can[UP*][UP*];
map<string,int> ids;
ISAP is; int id(string s) {
if(ids.count(s)) return ids[s];
int i = ids.size();
return ids[s] = i;
} void floyd() {
int n = ids.size();
for(int i = ; i < n; i++) can[i][i] = true;
for(int k = ; k < n; k++) {
for(int i = ; i < n; i++) {
for(int j = ; j < n; j++) {
can[i][j] |= can[i][k] && can[k][j];
}
}
}
} int main() {
int T, n, m, k;
char s[+], s2[+];
scanf("%d", &T);
while(T--) {
ids.clear();
memset(can, false, sizeof(can));
scanf("%d", &n);
for(int i = ; i < n; i++) {
scanf("%s", s);
recep[i] = id(s);
}
scanf("%d", &m);
for(int i = ; i < m; i++) {
scanf("%s%s", s2, s);
device[i] = id(s);
}
scanf("%d", &k);
for(int i = ; i < k; i++) {
scanf("%s%s", s, s2);
can[id(s)][id(s2)] = true;
} floyd();
int V = ids.size();
is.init(V+);
for(int i = ; i < m; i++) is.AddEdge(V, device[i], );
for(int i = ; i < n; i++) is.AddEdge(recep[i], V+, );
for(int i = ; i < m; i++) {
for(int t = ; t < n; t++) {
if(!can[device[i]][recep[t]]) continue;
is.AddEdge(device[i], recep[t], );
}
}
printf("%d\n", m - is.Maxflow(V, V+));
if(T) printf("\n");
}
return ;
}
UVa 753 - A Plug for UNIX(最大流)的更多相关文章
- UVa 753 A Plug for UNIX (最大流)
题意:给定 n 种插座,m种设备,和k个转换器,问你最少有几台设备不能匹配. 析:一个很裸的网络流,直接上模板就行,建立一个源点s和汇点t,源点和每个设备连一条边,每个插座和汇点连一条边,然后再连转换 ...
- ZOJ1157, POJ1087,UVA 753 A Plug for UNIX (最大流)
链接 : http://acm.hust.edu.cn/vjudge/problem/viewProblem.action? id=26746 题目意思有点儿难描写叙述 用一个别人描写叙述好的. 我的 ...
- POJ 1087 A Plug for UNIX / HDU 1526 A Plug for UNIX / ZOJ 1157 A Plug for UNIX / UVA 753 A Plug for UNIX / UVAlive 5418 A Plug for UNIX / SCU 1671 A Plug for UNIX (网络流)
POJ 1087 A Plug for UNIX / HDU 1526 A Plug for UNIX / ZOJ 1157 A Plug for UNIX / UVA 753 A Plug for ...
- UVA 753 - A Plug for UNIX(网络流)
A Plug for UNIX You are in charge of setting up the press room for the inaugural meeting of the U ...
- UVA 753 A Plug for UNIX(二分图匹配)
A Plug for UNIX You are in charge of setting up the press room for the inaugural meeting of the Unit ...
- UVA 753 A Plug for UNIX (最大流)
关键在建图,转换器连一条容量无限的边表示可以转化无数次,设备的插头连源点,插座连汇点. dinic手敲已熟练,输出格式又被坑,总结一下,输出空行多case的,一个换行是必要的,最后一个不加空行,有Te ...
- UVA 753 A Plug for UNIX 电器插座(最大基数匹配,网络流)
题意: 给n个插座,m个设备(肯定要插电了),k种转换头可无限次使用(注意是单向的),问有多少设备最终是不能够插上插座的? 分析: 看起来就是设备匹配插座,所以答案不超过m.这个题适合用网络流来解. ...
- UVA 753 A Plug for UNIX
最大流解决 . 设置源点 0,连接所有设备(device) .设备-插头 -汇点 #include <map> #include <set> #include <list ...
- UVA - 753 A Plug for UNIX(网络流)
题意 给定一些插头设备和插座,有一些方法可以把其中一些插头变成另一种插头.求无法匹配插座的插头设备个数. 题解 用\(map\)给每个字符串标号为\(a_i\)和\(b_i\). 读入每种改变插头的方 ...
随机推荐
- 文档转换为pdf格式帮助类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Word = M ...
- Gradle 学习(一)
引入插件 apply plugin: 'java' apply plugin: 'war' apply plugin: 'jetty' 如果希望使用jar来启动项目, 可以这样修改项目和插件属性. a ...
- Java Native Interface Specification Contents 翻译
https://docs.oracle.com/en/java/javase/12/docs/specs/jni/index.html Google翻译 第1章:简介 本章介绍Java Native ...
- 多文件上传CommonsMultipartResolver
1.CommonsMultipartResolver是spring里面提供的一个上传方式,效率我不知道,但是加入spring容器管理还是很不错的. 2.先看依赖包pom.xml <project ...
- express组件学习
一.express 可以做:web application.api... 特性: 适合写简单的路由系统 集成很多模板引擎 中间件系统 二.请求与响应 var express = require('ex ...
- mysql三表联合查询
-- SELECT d.userId, d.userPhoNum, a.orderId, a.productType, b.courseId, b.courseName, c.payJe -- FRO ...
- es6 import笔记
export输出: // profile.js var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; export ...
- JQ中的FormData对象 ajax上传文件
HTML代码: <form enctype="multipart/form-data" method="POST" name="searchfo ...
- 移动端meta标签的使用和设置
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale= ...
- vue.js与angular.js的区别(个人)
刚进入实训 讲师就要发一些什么比较高大上的东西,本人才疏学浅 浅浅的分享一下angularjs 和vue.js的区别.只是简单的理解一下 大神勿喷. 生实训之前学习的angular.js 只是理解了 ...