很奇怪, 看到网上用的都是匈牙利算法求最大基数匹配

紫书上压根没讲这个算法, 而是用最大流求的。

难道是因为第一个人用匈牙利算法然后其他所有的博客都是看这个博客的吗?

很有可能……

回归正题。

题目中只差一个数字的时候可以匹配, 然后求最少模板数。

那么肯定匹配的越多就越少, 也就是求最多匹配多少。

这个时候我就想到了二分图最大基数匹配。

那么很容易想到可以匹配的一组之间就连一条弧。

但问题是怎么分成两类??分类的目的是让同一类之间没有弧, 这样才是二分图。

后来发现因为匹配的一组只有一个数字不一样, 所以肯定1的个数不同(或者0)

那么我们就可以根据1的个数的奇偶分两类, 这样才能构造出二分图。

然后我就这么交了, 然后WA。

后来发现貌似模板可能会重复, 因为是集合, 满足互异性, 所以这些重复的肯定是要去掉的。

这是个大坑……

所以加模板的时候要判断有没有加过。

然后最大流求最大基数匹配就ok了!

#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
#include<algorithm>
#include<iostream>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std; const int MAXN = 2123;
struct Edge { int from, to, cap, flow; };
vector<Edge> edges;
vector<int> g[MAXN];
vector<string> num;
int cur[MAXN], h[MAXN], s, t, n, m; void AddEdge(int from, int to, int cap)
{
edges.push_back(Edge{from, to, cap, 0});
edges.push_back(Edge{to, from, 0, 0});
g[from].push_back(edges.size() - 2);
g[to].push_back(edges.size() - 1);
} bool bfs()
{
queue<int> q;
memset(h, 0, sizeof(h));
q.push(s);
h[s] = 1; while(!q.empty())
{
int x = q.front(); q.pop();
REP(i, 0, g[x].size())
{
Edge& e = edges[g[x][i]];
if(e.cap > e.flow && !h[e.to])
{
h[e.to] = h[x] + 1;
q.push(e.to);
}
}
} return h[t];
} int dfs(int x, int a)
{
if(x == t || a == 0) return a;
int flow = 0, f;
for(int& i = cur[x]; i < g[x].size(); i++)
{
Edge& e = edges[g[x][i]];
if(h[x] + 1 == h[e.to] && (f = dfs(e.to, min(e.cap - e.flow, a))) > 0)
{
e.flow += f;
edges[g[x][i] ^ 1].flow -= f;
flow += f;
if((a -= f) == 0) break;
}
}
return flow;
} int maxflow()
{
int flow = 0;
while(bfs()) memset(cur, 0, sizeof(cur)), flow += dfs(s, 1e9);
return flow;
} //从这以上都是求最大流 int sum(string s) //模板中1的个数
{
int ret = 0;
REP(i, 0, s.length()) ret += (s[i] == '1');
return ret & 1;
} bool judge(string s) //记住集合中不能有重复元素
{
REP(i, 0, num.size())
if(s == num[i])
return false;
return true;
} int main()
{
while(~scanf("%d%d", &n, &m) && n && m)
{
num.clear();
REP(i, 0, MAXN) g[i].clear();
edges.clear(); while(m--)
{
string s;
cin >> s;
if(s.find('*') == -1) { if(judge(s)) num.push_back(s); }
else
{
int p; for(p = 0; s[p] != '*'; p++); //拆成两个模板
s[p] = '0'; if(judge(s)) num.push_back(s);
s[p] = '1'; if(judge(s)) num.push_back(s);
}
}
s = num.size(); t = s + 1; //源点和汇点 REP(i, 0, num.size())
{
if(sum(num[i])) AddEdge(s, i, 1); //如果这个点是奇的, 那么从源点到奇数点连一条弧
else AddEdge(i, t, 1); // 偶的则从这个点到汇点连一条弧
REP(j, i + 1, num.size())
{
int cnt = 0;
REP(k, 0, n)
{
if(num[i][k] != num[j][k]) cnt++;
if(cnt > 1) break;
}
if(cnt == 1)
{
if(sum(num[i])) AddEdge(i, j, 1); //注意这里奇数点在左侧, 偶数点在右侧, 弧的方向不能错
else AddEdge(j, i, 1);
}
}
} printf("%d\n", num.size() - maxflow()); //总的模板数减去匹配数即是答案
} return 0;
}

紫书 习题 11-8 UVa 1663 (最大流求二分图最大基数匹配)的更多相关文章

  1. 紫书 习题 11-9 UVa 12549 (二分图最小点覆盖)

    用到了二分图的一些性质, 最大匹配数=最小点覆盖 貌似在白书上有讲 还不是很懂, 自己看着别人的博客用网络流写了一遍 反正以后学白书应该会系统学二分图的,紫书上没讲深. 目前就这样吧. #includ ...

  2. 紫书 习题8-12 UVa 1153(贪心)

    本来以为这道题是考不相交区间, 结果还专门复习了一遍前面写的, 然后发现这道题的区间是不是 固定的, 是在一个范围内"滑动的", 只要右端点不超过截止时间就ok. 然后我就先考虑有 ...

  3. 紫书 习题8-7 UVa 11925(构造法, 不需逆向)

    这道题的意思紫书上是错误的-- 难怪一开始我非常奇怪为什么第二个样例输出的是2, 按照紫书上的意思应该是22 然后就不管了,先写, 然后就WA了. 然后看了https://blog.csdn.net/ ...

  4. 紫书 习题 11-10 UVa 12264 (二分答案+最大流)

    书上写的是UVa 12011, 实际上是 12264 参考了https://blog.csdn.net/xl2015190026/article/details/51902823 这道题就是求出一种最 ...

  5. UVA 1594 Ducci Sequence(紫书习题5-2 简单模拟题)

    A Ducci sequence is a sequence of n-tuples of integers. Given an n-tuple of integers (a1, a2, · · · ...

  6. 紫书 习题7-8 UVa 12107 (IDA*)

    参考了这哥们的博客 https://blog.csdn.net/hyqsblog/article/details/46980287  (1)atoi可以char数组转int, 头文件 cstdlib ...

  7. 紫书 习题 11-17 UVa 1670 (图论构造)

    一开始要符合题目条件, 那么肯定没有任何一个点是孤立的, 也就是说没有点的度数是1 所以我就想让度数是1的叶子节点相互连起来.然后WA 然后看这哥们的博客 https://blog.csdn.net/ ...

  8. 紫书 习题 8-21 UVa 1621 (问题分析方法)

    知道是构造法但是想了挺久没有什么思路. 然后去找博客竟然只有一篇!!https://blog.csdn.net/no_name233/article/details/51909300 然后博客里面又说 ...

  9. 紫书 习题8-18 UVa 11536 (扫描法)

    这道题貌似可以用滑动窗口或者单调栈做, 但是我都没有用到. 这道题要求连续子序列中和乘上最小值最大, 那么我们就可以求出每一个元素, 以它为最小值的的最大区间的值, 然后取max就ok了.那么怎么求呢 ...

随机推荐

  1. linux C++ 编译错误 file not found 其实是原文件后缀的问题

    gcc和clang会根据源文件的后缀.c或者.cpp判断原文件类型,采取不同的编译策略,所以我使用它们编译后缀是.c的C++原文件的时候会出现找不到include的文件的错误,使用正确的后缀名即可.同 ...

  2. [NOIP补坑计划]NOIP2017 题解&做题心得

    终于做完了…… 场上预计得分:?(省一分数线:295) 由于看过部分题解所以没有预计得分qwq 题解: D1T1 小凯的疑惑 题面 震惊!一道小学奥数题竟难倒无数高中考生! 欢迎大家以各种姿势*和谐* ...

  3. BZOJ 3413 匹配 (后缀自动机+线段树合并)

    题目大意: 懒得概括了 神题,搞了2个半晚上,还认为自己的是对的...一直调不过,最后终于在jdr神犇的帮助下过了这道题 线段树合并该是这道题最好理解且最好写的做法了,貌似主席树也行?但线段树合并这个 ...

  4. 详解 QT 主要类 QWidget

    QWidget类是所有用户界面对象的基类,每一个窗口部件都是矩形,并且它们按Z轴顺序排列的.一个窗口部件可以被它的父窗口部件或者它前面的窗口部件盖住一部分. 先来看内容. AD: 2013云计算架构师 ...

  5. 树形dp复习 树上依赖背包问题

    选课 今天又看了一下这道题,竟然AC不了了 自己的学习效率有点低下 要明白本质,搞透彻 #include<bits/stdc++.h> #define REP(i, a, b) for(r ...

  6. jvm 虚拟机参数_新生代内存分配

    1.参数 -Xmn 设置新生代的大小,设置一个比较大的新生代会减少老年代的大小,这个参数对系统性能以及 GC 行为影响很大,新生代大小一般设置为真个堆内存的1/3到1/4 -XX:SurvivorRa ...

  7. l洛谷 P2326 AKN’s PPAP

    P2326 AKN’s PPAP 题目描述 “I have a pen,I have an apple.Eh,Apple-Pen!. I have a pen,I have pineapple.En, ...

  8. [HTML5] Inlining images with SVG and data URIs

    The main reason you want to do I"nlining images with SVG and data URIs" is to reduce http ...

  9. hdu 5077 NAND(打表)2014 Asia regional 鞍山站 H题

    题目链接:点击打开链接 题意:就是一个按位运算的一个函数.问最少经过多少步运算能够得到给定数. 思路:不是我投机取巧想打表.是特么这题仅仅能打表.. .打表思想用能够得到的数的集合表示状态bfs:最后 ...

  10. 【iOS】UICollectionView自己定义Layout之蜂窝布局

    网上的UICollectionView的Layout布局,其cell的形状多为矩形和圆形. 本篇博文将正六边形作为cell的基本形状,为您展现独特的蜂窝布局效果及实现源代码. 帮助您让自己的App脱颖 ...