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\). 读入每种改变插头的方 ...
随机推荐
- MySQL wait_timeout参数设置与网上常见错误小纠
discard connection com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link fail ...
- js校验数字是否为小数
js校验数字是否为小数: function checkDot(c) {c = parseFloat(c); -]?[-]*\.[-]*[-]+$/; return r.test(c); }
- iOS之nib、xib及storyboard的区别及storyboard的加载过程
先讲述下nib, nib是3.0版本以前的产物,在终端下我们可以看到,NIB其实是一个文件夹,里面有可执行的二进制文件: 区分xib和storyboard的区别? 不同点: 1> 无论nib也好 ...
- Mysql 删除数据表重复行
准备示例数据 以下sql创建表,并将示例数据插入到用于演示的contacts表中. CREATE TABLE contacts ( id INT PRIMARY KEY AUTO_INCREMENT, ...
- input的属性用法介绍
Input表示Form表单中的一种输入对象,其又随Type类型的不同而分文本输入框,密码输入框,单选/复选框,提交/重置按钮等,下面一一介绍. 1,type=text 输入类型是text,这是我们见的 ...
- 在JSP中将EXEL文件的数据传入到数据库中
在jsp中: 在script中使用函数: $(function(){ //var lpyear = document.getElementById("lpyear").value; ...
- css3之背景定位
属性: background-position: left top || left bottom || right top || right bottom || center center || 像素 ...
- SPOJ QTREE6
题意 给你一棵\(n\)个点的树,编号\(1\)~\(n\).每个点可以是黑色,可以是白色.初始时所有点都是黑色.有两种操作 \(0\ u\):询问有多少个节点\(v\)满足路径\(u\)到\(v\) ...
- js实现数组内数据的上移和下移
var swapItems = function(arr, index1, index2){ arr[index1] = arr.splice(index2,1,arr[index1])[0] ret ...
- 使用dojo.connect()添加事件的注意事项
使用dojo.connect()添加事件处理器是很方便的,不用再考虑跨浏览器的问题了.但要想正确地使用这个方法,仍然要注意几个问题: 1.用dojo.byId()获取的是dom元素,而用 ...