题意:
有n个插座,m个设备以及k种转化器(每种转化器视为有无限个)。
转换器A->B可以将A类型的插头转化成B类型的插头,所以可以插在B类型的插座上。
求最少剩多少不匹配的设备。
分析:
抛开转换器不讲,插头插在插座上就是一个最大二分图匹配。
可以用最大流的算法,增加一个连接每个插头的源点s和连接每个插座的汇点t,每条弧容量都为1.
然后求最大流量,就是二分图的最大基数匹配。
既然有了转换器,一种插头可以用转换器转换成多种类型的插头,所以可以Floyd一次,求出每种插头可以转换成的所有的插头类型。
然后构二分图,求最大匹配即可。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 400 + 10; 6 7 vector<string> names; 8 int ID(const string& s) 9 { 10 for(int i = 0; i < names.size(); ++i) 11 if(names[i] == s) return i; 12 names.push_back(s); 13 return names.size() - 1; 14 } 15 16 int n, m, k; 17 bool d[maxn][maxn]; 18 int target[maxn], device[maxn]; 19 20 const int INF = 1000000000; 21 22 struct Edge 23 { 24 int from, to, cap, flow; 25 Edge(int u=0, int v=0, int c=0, int f=0):from(u), to(v), cap(c), flow(f) {} 26 }; 27 28 struct EdmondsKarp 29 { 30 int n, m; 31 vector<Edge> edges; 32 vector<int> G[maxn]; 33 int a[maxn]; 34 int p[maxn]; 35 36 void Init(int n) 37 { 38 for(int i = 0; i < n; ++i) G[i].clear(); 39 edges.clear(); 40 } 41 42 void AddEdge(int from, int to, int cap) 43 { 44 edges.push_back(Edge(from, to, cap, 0)); 45 edges.push_back(Edge(to, from, 0, 0)); 46 m = edges.size(); 47 G[from].push_back(m-2); 48 G[to].push_back(m-1); 49 } 50 51 int MaxFlow(int s, int t) 52 { 53 int flow = 0; 54 for(;;) 55 { 56 memset(a, 0, sizeof(a)); 57 queue<int> Q; 58 Q.push(s); 59 a[s] = INF; 60 while(!Q.empty()) 61 { 62 int x = Q.front(); Q.pop(); 63 for(int i = 0; i < G[x].size(); ++i) 64 { 65 Edge& e = edges[G[x][i]]; 66 if(!a[e.to] && e.cap > e.flow) 67 { 68 p[e.to] = G[x][i]; 69 a[e.to] = min(a[x], e.cap - e.flow); 70 Q.push(e.to); 71 } 72 } 73 if(a[t]) break; 74 } 75 if(!a[t]) break; 76 for(int u = t; u != s; u = edges[p[u]].from) 77 { 78 edges[p[u]].flow += a[u]; 79 edges[p[u]^1].flow -= a[u]; 80 } 81 flow += a[t]; 82 } 83 return flow; 84 } 85 }; 86 87 EdmondsKarp g; 88 89 int main() 90 { 91 //freopen("in.txt", "r", stdin); 92 int T; 93 scanf("%d", &T); 94 while(T--) 95 { 96 names.clear(); 97 memset(d, 0, sizeof(d)); 98 string s1, s2; 99 scanf("%d", &n); 100 for(int i = 0; i < n; ++i) { cin >> s1; target[i] = ID(s1); } 101 scanf("%d", &m); 102 for(int i = 0; i < m; ++i) { cin >> s1 >> s2; device[i] = ID(s2); } 103 scanf("%d", &k); 104 for(int i = 0; i < k; ++i) { cin >> s1 >> s2; d[ID(s1)][ID(s2)] = true; } 105 //Floyd 106 int V = names.size(); 107 for(int k = 0; k < V; ++k) 108 for(int i = 0; i < V; ++i) 109 for(int j = 0; j < V; ++j) 110 d[i][j] |= d[i][k] && d[k][j]; 111 112 //Build Graph 113 g.Init(V+2); 114 for(int i = 0; i < m; ++i) g.AddEdge(V, device[i], 1);//源点到每个设备 115 for(int i = 0; i < n; ++i) g.AddEdge(target[i], V+1, 1);//每个插座到汇点 116 for(int i = 0; i < m; ++i) 117 for(int j = 0; j < n; ++j) 118 if(d[device[i]][target[j]]) g.AddEdge(device[i], target[j], INF); 119 int ans = g.MaxFlow(V, V+1); 120 printf("%d ", m-ans); 121 if(T) puts(""); 122 } 123 124 return 0; 125 }
紫书后面又介绍了一种更简单的做法,就是直接把k种转换器对应的k条弧加到图中去,然后求最大流。
代码就不贴了,=_=||