UVa1663 Purifying Machine (二分图最大匹配,最大流)

链接:http://vjudge.net/problem/UVA-1663

分析:先把原来m个模板串取出来扔进set里去重,然后将去重后的匹配串从1~cnt编号。题目说每个模板串最多一个星号“*”,所以说一个匹配串只有唯一的一个跟它只有一个位上的数字不同的另一个匹配串组合成一个模板串,这就成了二分图最大匹配问题了。然后针对每个匹配串找出和它只有一位数字不同的其它匹配串,把其编号存进数组vector<int> e[i]里。然后对cnt个匹配串以e[i][j]为状态转移进行dfs黑白染色,这样就把这些匹配串分成了左右两列,同一列中的匹配串至少也有两个位上的数字不同不可能组合成一个模板串,满足二分图的要求,然后从源点s向白色结点连一条容量为1的弧,从黑色结点向汇点t连一条容量为1的弧,对于白色结点所能匹配的所有黑色结点,都连一条容量为1的弧。跑一个s-t的最大流,得到匹配对数。再用cnt减去匹配对数即为答案。

  1 #include <iostream>
  2 #include <string>
  3 #include <set>
  4 #include <queue>
  5 #include <cstring>
  6 #include <vector>
  7 using namespace std;
  8
  9 const int maxn = 1000 + 5;
 10 const int INF = 0x3f3f3f3f;
 11
 12 struct Edge {
 13     int from, to, cap, flow;
 14     Edge(int u, int v, int c, int f):from(u), to(v), cap(c), flow(f) {}
 15 };
 16
 17 struct EdmondsKarp {
 18     int n, m;
 19     vector<Edge> edges;
 20     vector<int> G[maxn];
 21     int a[maxn];
 22     int p[maxn];
 23
 24     void init(int n) {
 25         for (int i = 0; i < n; i++) G[i].clear();
 26         edges.clear();
 27     }
 28
 29     void AddEdge(int from, int to, int cap) {
 30         edges.push_back(Edge(from, to, cap, 0));
 31         edges.push_back(Edge(to, from, 0, 0));
 32         m = edges.size();
 33         G[from].push_back(m - 2);
 34         G[to].push_back(m - 1);
 35     }
 36
 37     int Maxflow(int s, int t) {
 38         int flow = 0;
 39         for (;;) {
 40             memset(a, 0, sizeof(a));
 41             queue<int> Q;
 42             Q.push(s);
 43             a[s] = INF;
 44             while (!Q.empty()) {
 45                 int x = Q.front(); Q.pop();
 46                 for (int i = 0; i < G[x].size(); i++) {
 47                     Edge& e = edges[G[x][i]];
 48                     if (!a[e.to] && e.cap > e.flow) {
 49                         p[e.to] = G[x][i];
 50                         a[e.to] = min(a[x], e.cap - e.flow);
 51                         Q.push(e.to);
 52                     }
 53                 }
 54                 if (a[t]) break;
 55             }
 56             if (!a[t]) break;
 57             for (int u = t; u != s; u = edges[p[u]].from) {
 58                 edges[p[u]].flow += a[t];
 59                 edges[p[u] ^ 1].flow -= a[t];
 60             }
 61             flow += a[t];
 62         }
 63         return flow;
 64     }
 65 };
 66
 67 EdmondsKarp g;
 68
 69 int n, m;
 70 string s[maxn];
 71 set<string> st;
 72 vector<int> e[maxn * 2];
 73 int color[maxn];
 74
 75 bool ismatch(string& a, string& b) {
 76     int cnt = 0;
 77     for (int i = 0; i < n; i++)
 78         if (a[i] != b[i]) cnt++;
 79     if (cnt == 1) return true;
 80     else return false;
 81 }
 82
 83 void AddColor(int s, int c) {
 84     color[s] = c;
 85     for (int i = 0; i < e[s].size(); i++) {
 86         int t = e[s][i];
 87         if (!color[t]) AddColor(t, 3 - c);
 88     }
 89 }
 90
 91 int main() {
 92     while (cin >> n >> m && (n || m)) {
 93         for (int i = 0; i < m; i++) cin >> s[i];
 94         // for (int i = 0; i < m; i++) cout << s[i] << endl;
 95         st.clear();
 96         for (int i = 0; i < m; i++) {
 97             int j = 0;
 98             while (j < n) {
 99                 if (s[i][j] == ‘*‘) {
100                     s[i][j] = ‘0‘;
101                     st.insert(s[i]);
102                     s[i][j] = ‘1‘;
103                     st.insert(s[i]);
104                     break;
105                 }
106                 j++;
107             }
108             if (j == n) st.insert(s[i]);
109         }
110         // cout << "ok" << endl;
111         int cnt = 0;
112         for (set<string>:: iterator it = st.begin(); it != st.end(); it++)
113             s[++cnt] = *it;
114        // for (int i = 1; i <= cnt; i++) cout << s[i] << endl;
115         for (int i = 1; i <= cnt; i++) e[i].clear();
116         for (int i = 1; i <= cnt; i++)
117             for (int j = i + 1; j <= cnt; j++)
118                 if (ismatch(s[i], s[j])) {
119                     e[i].push_back(j);
120                     e[j].push_back(i);
121                 }
122         memset(color, 0, sizeof(color));
123         for (int i = 1; i <= cnt; i++) if (!color[i]) AddColor(i, 1);
124         // for (int i = 1; i <= cnt; i++) cout << color[i] << endl;
125         g.init(cnt + 2);
126         for (int i = 1; i <= cnt; i++) {
127             if (color[i] == 1) {
128                 g.AddEdge(0, i, 1);
129                // g.AddEdge(i, 0, 0);
130                 for (int j = 0; j < e[i].size(); j++) {
131                     int v = e[i][j];
132                     g.AddEdge(i, v, 1);
133                   //  g.AddEdge(v, i, 0);
134                 }
135             }
136             if (color[i] == 2) {
137                 g.AddEdge(i, cnt + 1, 1);
138                // g.AddEdge(cnt + 1, i, 0);
139             }
140         }
141         // cout << g.Maxflow(0, cnt + 1) << endl;
142         cout << cnt - g.Maxflow(0, cnt + 1) << endl;
143     }
144     return 0;
145 }
时间: 2024-10-25 13:01:41

UVa1663 Purifying Machine (二分图最大匹配,最大流)的相关文章

POJ - 2724 Purifying Machine 二分图 最大匹配

题目大意:看了别人的题意: 给出m串长度为n的01串,如果某个串中包含'*'号的,那么这个串就可以变成两个串(因为'*'既可以表示0或者1) 比如字符串*01就可以表示成001,或者101 现在的任务是消除所有的串,给出消除串的规则如下: 1一次消灭一个串. 2如果两个串的差别只有一位的话可以同时消灭这两个串. 问至少需要多少次才能消灭这些串 解题思路:先将这些串去重,化成整数,这样就可以得到两个点集了,点集之间的关系就是一个操作能否消除掉两个串,如果可以的话,则这两个串就存在关系了 #incl

UVA1663 Purifying Machine

模版集合个数减少是因为匹配串集合中没被匹配过的一对串匹配了.所以就是找一个二分图最大匹配. 代码里没有显式地建立二分图,可以假想两个集合X和Y,由于两个集合都会跑一遍,所以一个匹配会被算两次,返回的时候除以2就行了. 拾起几乎快忘了的匈牙利算法... #include<bits/stdc++.h> using namespace std; const int maxn = 10; const int N = 1<<maxn; #define debug(x)\ bitset<

Ural1109_Conference(二分图最大匹配/匈牙利算法/网络最大流)

解题报告 二分图第一题. 题目描述: 为了参加即将召开的会议,A国派出M位代表,B国派出N位代表,(N,M<=1000) 会议召开前,选出K队代表,每对代表必须一个是A国的,一个是B国的; 要求每一个代表要与另一方的一个代表联系,除了可以直接联系,也可以电话联系,求电话联系最少 思路: 电话联系最少就要使直接联系最大,又是一一匹配关系,就是二分图的最大匹配. 下面是匈牙利算法. #include <cstdio> #include <cstring> #include <

【二分】【字符串哈希】【二分图最大匹配】【最大流】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem I. Minimum Prefix

给你n个字符串,问你最小的长度的前缀,使得每个字符串任意循环滑动之后,这些前缀都两两不同. 二分答案mid之后,将每个字符串长度为mid的循环子串都哈希出来,相当于对每个字符串,找一个与其他字符串所选定的子串不同的子串,是个二分图最大匹配的模型,可以匈牙利或者Dinic跑最大流看是否满流. 一个小优化是对于某个字符串,如果其所有不同的子串数量超过n,那么一定满足,可以直接删去. 卡常数,不能用set,map啥的,采取了用数组记录哈希值,排序后二分的手段进行去重和离散化. #include<cst

ZOJ 1364 Machine Schedule(二分图最大匹配)

题意 机器调度问题 有两个机器A,B A有n种工作模式0...n-1 B有m种工作模式0...m-1 然后又k个任务要做 每个任务可以用A机器的模式i或b机器的模式j来完成 机器开始都处于模式0 每次换模式时都要重启 问完成所有任务机器至少重启多少次 最基础的二分图最大匹配问题 对于每个任务把i和j之间连一条边就可以构成一个二分图 那么每个任务都可以对应一条边 那么现在就是要找最少的点 使这些点能覆盖所有的边 即点覆盖数 又因为二分图的点覆盖数 = 匹配数 那么就是裸的求二分图最大匹配问题了 两

POJ1698_Alice&#39;s Chance(二分图多重最大匹配/最大流)

解题报告 http://blog.csdn.net/juncoder/article/details/38237641 题目传送门 题意: N个电影,每个电影在每一周有固定拍映时间,电影必须在W周前演完.有一个演员,他每天只能演一场电影,对于每部电影必须演完D天才算完. 思路: 二分图多重最大匹配问题,对于每个电影,源点与每个电影连上一条边容量为D,电影与每周7天对应拍映连线,容量为1,每周每天与汇点连线容量为1 在二分图最大匹配中,每个点(不管是X方点还是Y方点)最多只能和一条匹配边相关联,然

POJ 1325 Machine Schedule 二分图最大匹配

把每一个任务看做一个边,机器的模式看做是一个点,这个其实就是求一个最少点覆盖所有边即最小点覆盖集的问题,因为最小点覆盖集=二分图的最大匹配,所以问题转化成了求二分图最大匹配问题. 第一次写二分图匹配,感觉建模还是相当困难的. #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <strin

POJ2724 Purifying Machine【二分图最小边覆盖】

题目链接: http://poj.org/problem?id=2724 题目大意: 有2^N个奶酪,编号从000-00到111-11,现在有台机器有N个开关,每个开关的状态有3个, 分别是'0'.'1'.'*',每个开关只能有一个状态存在.'*'状态可以替代'0'或'1'状态.比如11*1, 对应为1111或1101.现在有M个奶酪被感染了,每个奶酪的编号为长度为N的二进制字符串. 和开关一样,每一位上除了能为'1'.'0'之外,还可以是'*',表示既能是'0',也能是'1'.比如说 1*1,

它处资料:二分图最大匹配的匈牙利算法

资料出处:点击打开链接 匈牙利算法 二分图最大匹配的匈牙利算法:  二分图是这样一个图,它的顶点能够分类两个集合X和Y,全部的边关联在两个顶点中.恰好一个属于集合X.还有一个属于集合Y. 最大匹配: 图中包括边数最多的匹配称为图的最大匹配. 完美匹配: 假设全部点都在匹配边上.称这个最大匹配是完美匹配. 最小覆盖: 最小覆盖要求用最少的点(X集合或Y集合的都行)让每条边都至少和当中一个点关联.能够证明:最少的点(即覆盖数)=最大匹配数 最小路径覆盖: 用尽量少的不相交简单路径覆盖有向无环图G的全