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\). 读入每种改变插头的方 ...
随机推荐
- viewport其实没那么难理解
在学习移动端布局的时候,你肯定听说过"viewport"这个词,然后去问度娘或谷歌.你会惊奇的发现,这个viewport不简单,居然有那么多兄弟——layout viewport. ...
- Python-常用模块及简单的案列
1.模块 函数的优点之一,就是可以使用函数将代码块与主程序分离,通过给函数指定一个描述性的名称,并将函数存储在被称为模块的独立文件中,再将模块导入主程序中,通过import语句允许在当前运行的程序 ...
- 推荐几款基于Bootstrap的响应式后台管理模板
1.Admin LTE 该模版开源免费. AdminLTE - 是一个完全响应式管理模板.基于Bootstrap3的框架.高度可定制的,易于使用.支持很多的屏幕分辨率适合从小型移动设备到大型台式机. ...
- Jquery UI Dialog 导致C#页面后台事件失效
$(function () { dialog = $("#dialog-form").dialog({ autoOpen: false, height: 450, width: 5 ...
- [android] 练习PopupWindow实现对话框
练习使用Dialog实习对话框 package com.example.tsh; import android.app.Activity; import android.app.Dialog; imp ...
- 《MySQL 基础课程》笔记整理(基础篇)
一.尝试MySQL 1.打开MySQL # 启动MySQL服务 sudo service mysql start # 使用 root 用户登录,这里密码为空,直接回车登录 mysql -u root ...
- Spring系列之Alias标签的解析与使用
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- LinearLayout中的android:layout_garvity的center_vertical和center_horizontal
当LinearLayout的排列方向是 horizontal时,只有垂直方向上的对齐方式才会生效.因为此时水平方向上的长度是不固定的,每添加一个控件,水平方向上的长度都会改变,因而无法指定该方向上的对 ...
- 配置Spring
搭建Springmvc的时候,出现异常: IOException parsing XML document from ServletContext resource [/WEB-INF/applica ...
- vue-cli项目打包多个与static文件同级的静态资源目录(copy-webpack-plugin插件的使用)
场景 业务要求能够直接通过 "域名+/file"的方式访问静态资源的html,然而产品绝对static暴露在url中不好看又不能直接将html放在static中.所以想到了既然st ...