0x69 图论-二分图的覆盖与独立集
A:Machine Schedule

输入
5 5 10
0 1 1
1 1 2
2 1 3
3 1 4
4 2 1
5 2 2
6 2 3
7 2 4
8 3 3
9 4 3
0
输出
3
在二分图中我们经常要找题目中的 “0要素” 和 “1要素” ,作为解答的突破口。
二分图最小覆盖模型的特点则是:每条边有2个端点,二者至少选择一个。我们不妨称之为 “2元素”。
如果题目具有 “2元素” 的特点,那么可以尝试抽象成二分图的最小覆盖模型求解。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int n, m, k, f[N], ans;
bool v[N];
vector<int>e[N];
bool dfs(int x) {
for (int i = 0; i < e[x].size(); ++i) {
int y = e[x][i];
if (v[y])continue;
v[y] = true;
if (!f[y] || dfs(f[y])) {
f[y] = x;
return 1;
}
}
return 0;
}
int main() {
//freopen("in.txt", "r", stdin);
while (cin >> n && n) {
cin >> m >> k;
for (int i = 0; i < n; ++i)e[i].clear();
for (int i = 0; i < k; ++i) {
int x, y;
cin >> i >> x >> y;
if (x && y)e[x].push_back(y);
}
memset(f, 0, sizeof f);
ans = 0;
for (int i = 1; i < n; ++i) {
memset(v, 0, sizeof v);
ans += dfs(i);
}
cout << ans << endl;
}
}
B. Muddy Fields
题目描述

输入
4 4
*.*.
.***
***.
..*.
输出
4
备注:
OUTPUT DETAILS:
Boards 1, 2, 3 and 4 are placed as follows:
1.2.
.333
444.
..2.
Board 2 overlaps boards 3 and 4.
题目大意:用木板将'*'覆盖,同一行或同一列的'*'可以用一块木板覆盖,'.'不能被覆盖。问最少用多少块木板可以把全部的'*'覆盖?
木板只能够覆盖连续的横着的泥巴和竖着的泥巴,中间有草地就要隔开
解题思路:二分匹配的经典构图题目
构图思路:
将横着的木板和看成一边的点的集合,将竖着的木板看成另外一边的点的集合,如果他们相交于一点就连边
如果要把所有的泥巴覆盖,又要所需要的木板最少,那么就是求最小点覆盖
所以用匈牙利求最大匹配数即可
构图的代码要好好再看看!
#include<bits/stdc++.h>
using namespace std;
const int N = 56;
int n, m, tot = 1, a[N][N][2], f[N * N], ans;
char s[N][N];
bool v[N * N];
vector<int> e[N * N];
bool dfs(int x) {
for (unsigned int i = 0; i < e[x].size(); i++) {
int y = e[x][i];
if (v[y]) continue;
v[y] = 1;
if (!f[y] || dfs(f[y])) {
f[y] = x;
return 1;
}
}
return 0;
}
int main() {
//freopen("in.txt", "r", stdin);
cin >> n >> m;
for (int i = 1; i <= n; i++) scanf("%s", s[i] + 1);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m + 1; j++)//m + 1
if (s[i][j] == '*') a[i][j][0] = tot;
else ++tot;
int t = tot;
for (int j = 1; j <= m; j++)
for (int i = 1; i <= n + 1; i++)
if (s[i][j] == '*') a[i][j][1] = tot;
else ++tot;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (s[i][j] == '*') {
e[a[i][j][0]].push_back(a[i][j][1]);
e[a[i][j][1]].push_back(a[i][j][0]);
}
for (int i = 1; i < t; i++) {
memset(v, 0, sizeof(v));
ans += dfs(i);
}
cout << ans << endl;
}
C. 骑士放置 (二分图的最大独立集)
题目描述

输入
2 3 0
输出
4
若两个格子是“日”字的对角(能相互攻击),则在它们对应的节点之间连边。
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int n, m, t, ans, fx[N][N], fy[N][N];
bool a[N][N], v[N][N];
const int dx[8] = { -2, -2, -1, -1, 1, 1, 2, 2 };
const int dy[8] = { -1, 1, -2, 2, -2, 2, -1, 1 };
bool dfs(int x, int y) {
for (int i = 0; i < 8; i++) {
int nx = x + dx[i], ny = y + dy[i];
if (nx < 1 || ny < 1 || nx > n || ny > m || a[nx][ny]) continue;
if (v[nx][ny]) continue;
v[nx][ny] = 1;
if (fx[nx][ny] == 0 || dfs(fx[nx][ny], fy[nx][ny])) {
fx[nx][ny] = x, fy[nx][ny] = y;
return true;
}
}
return false;
}
int main() {
//freopen("in.txt", "r", stdin);
cin >> n >> m >> t;
for (int i = 0; i < t; ++i) {
int x, y; cin >> x >> y;
a[x][y] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (i + j & 1) continue;
if (a[i][j]) continue;
memset(v, 0, sizeof(v));
if (dfs(i, j)) ans++;
}
}
cout << n * m - t - ans << endl;
}
再贴一个网络流的写法 7ms,不得不说匈牙利算法在解决这类问题稍微时间复杂度大了点
#include<bits/stdc++.h>
using namespace std;
#define MAXN 60555
#define MAXM 520555
int N, M, TT;
int hd[MAXN], val[MAXM], nxt[MAXM], to[MAXM], tot(1);
int cur[MAXN];
int S, T, x, y;
bool mp[205][205];
void Add( int x, int y, int z ){
nxt[++tot] = hd[x]; hd[x] = tot; to[tot] = y; val[tot] = z;
nxt[++tot] = hd[y]; hd[y] = tot; to[tot] = x; val[tot] = 0;
}
int d[MAXN]; queue<int> Q;
bool BFS(){
while( !Q.empty() ) Q.pop();
memset( d, 0, sizeof d ); d[S] = 1; Q.push(S);
while( !Q.empty() ){
int t(Q.front()); Q.pop();
for ( int i = hd[t]; i; i = nxt[i] ){
if ( val[i] && !d[to[i]] ){
d[to[i]] = d[t] + 1; Q.push(to[i]);
if ( to[i] == T ) return 1;
}
}
}
return 0;
}
int DFS( int x, int fl ){
if ( x == T ) return fl;
int res(fl), k;
for ( int &i = cur[x]; i && res; i = nxt[i] ){
if ( val[i] && d[to[i]] == d[x] + 1 ){
k = DFS( to[i], min( res, val[i] ) );
if ( !k ) d[to[i]] = 0;
res -= k; val[i] -= k; val[i ^ 1] += k;
}
}
return fl - res;
}
int GetID( int x, int y ){ return ( x - 1 ) * M + y; }
int dir[][2] = { 1, 2, 2, 1, -1, -2, -2, -1, -1, 2, 2, -1, 1, -2, -2, 1 };
int main(){
scanf( "%d%d%d", &N, &M, &TT );
for ( int i = 1; i <= TT; ++i ) scanf( "%d%d", &x, &y ), mp[x][y] = 1;
S = 0; T = N * M + 1;
for ( int i = 1; i <= N; ++i )
for ( int j = 1; j <= M; ++j ){
if ( mp[i][j] ) continue;
if ( ( i ^ j ) & 1 ) Add( S, GetID( i, j ), 1 );
else{
Add( GetID( i, j ), T, 1 );
for ( int k = 0; k < 8; ++k ){
int tx(i + dir[k][0]), ty(j + dir[k][1]);
if ( tx > 0 && ty > 0 && tx <= N && ty <= M && !mp[tx][ty] ) Add( GetID( tx, ty ), GetID( i, j ), INT_MAX );
}
}
}
int ans( N * M - TT ), k;
while( BFS() ){
memcpy( cur, hd, sizeof hd );
while( ( k = DFS( S, INT_MAX ) ) ) ans -= k;
}
printf( "%d\n", ans );
return 0;
}
D. Vani和cl2捉迷藏 有向无环图的最小路径点覆盖
题目描述

输入
7 5
1 2
3 2
2 4
4 5
4 6
输出
3
首先明确,求解的是一个最大的点集,满足集合中的点中任意两个点之间没有通路。ohhhh???这不是最大独立集吗???可惜这是个有向图,最大独立集也是针对无向图来说的,如果你去找二分图的定义,会发现,前面都有一个前提,一个无向图怎样怎样,也就是说二分图是对无向图而言的。那这个有向图就不能当作最大独立集考虑了。
但是我们知道有向图有最小路径点覆盖,定义如下:选取最少的不相交的边覆盖全部顶点。最小路径可重复覆盖:选取最少可相交的边覆盖全部顶点。对于这个路径的集合,每次从里面最多挑出来一个点,如果挑出来多余一个点,以两个为例,那么这两个点之间肯定有一条简单路径可以连接,也就是说二者肯定有一方可以到达另一方。那么我们从所有的路径中,每个路径挑一个点,最后就组成了这个最大的点集合。
还有一点需要说明,为什么这个问题是一个最小路径可重复覆盖,因为题目中有说,如果一个点沿着某一路径走下去可以到达另一个点,则这两个点也是互相可以望见的,也就是说对于边a->b,b->c,间接的a->c也算。所以就先floyd求一下传递闭包,建立拆点二分图,然后跑一遍DAG最小路径点覆盖。(这类问题博主更习惯于直接用邻接矩阵QAQ)
#include <bits/stdc++.h>
#define mem(a, b) memset(a, b, sizeof a)
using namespace std;
const int N = 210;
bool w[N][N];
int match[N];
bool vis[N];
int n, m;
bool dfs(int x){
for (int i = 1; i <= n; i++){
if (w[x][i] && !vis[i]){
vis[i] = 1;
if (match[i] == -1 || dfs(match[i])){
match[i] = x;
return 1;
}
}
}
return 0;
}
void pre_work(){
mem(match, -1);
mem(vis, 0);
for (int k = 1; k <= n; k++){
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
w[i][j] = (w[i][k] && w[k][j]) || w[i][j];
}
}
}
}
int solve(){
int ans = n;
for (int i = 1; i <= n; i++){
mem(vis, 0);
if (dfs(i))ans--;
}
return ans;
}
int main()
{
mem(w, 0);
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i++){
int x, y;
scanf("%d %d", &x, &y);
w[x][y] = 1;
}
pre_work();
printf("%d\n", solve());
return 0;
}
0x69 图论-二分图的覆盖与独立集的更多相关文章
- 【BZOJ 3997】 3997: [TJOI2015]组合数学 (DP| 最小链覆盖=最大点独立集)
3997: [TJOI2015]组合数学 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 919 Solved: 664 Description 给出 ...
- 【codevs1907】【方格取数3】二分图最大带权独立集
[pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=59001242 向大(hei)佬(e)势力学(di ...
- 【题解】POJ3041 Asteroids - 图论 - 二分图匹配
声明:本博客所有题解都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。 POJ3041 Asteroids 题目描述 假如你现在正处在一个 \(N*N\ ...
- 图论 - 二分图的判断(dfs染色法)
二分图的判断(dfs染色法) 如何判断一个图是否为二分图 普通染色法模板 C++ 代码模板如下 思想:先将当前点染色,然后再将该点相连的结点进行染另外一种颜色 下面附上自己画的一张图假设我们从第一个点 ...
- 图论(二分图,KM算法):HDU 3488 Tour
Tour Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)Total Submis ...
- 图论--二分图最佳完美匹配(KM模板)
#include <iostream> #include <cstring> #include <cstdio> using namespace std; cons ...
- [学习笔记]最小割之最小点权覆盖&&最大点权独立集
最小点权覆盖 给出一个二分图,每个点有一个非负点权 要求选出一些点构成一个覆盖,问点权最小是多少 建模: S到左部点,容量为点权 右部点到T,容量为点权 左部点到右部点的边,容量inf 求最小割即可. ...
- SCU3185 Black and white(二分图最大点权独立集)
题目大概说有几个黑色.白色矩阵,问能选出黑白不相交的矩形面积和的最大值. 建二分图,黑色矩阵为X部的点,白色为Y部,XY的点权都为其矩阵面积,如果有个黑白矩阵相交则它们之间有一条边,那样问题就是要从这 ...
- 【最大点权独立集】【HDU1565】【方格取数】
题目大意: 给你一个n*n的格子的棋盘,每个格子里面有一个非负数. 从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大. 初看: 没想法 ...
- DP&图论 DAY 2 下午
DP&图论 DAY 2 下午 基础树形DP 前言◦ 1:与树或图的生成树相关的动态规划.◦ 2:以每棵子树为子结构,在父亲节点合并,注意树具有天然的子结构.这是很优美的很利于dp的.◦ 3 ...
随机推荐
- 「有问必答」秒杀系统 Go并发编程实践!
有问必答 摘要 本文将介绍如何使用Go语言的并发原语来构建一个简单的高并发秒杀系统. 我们将使用Go语言的原生库和一些常见的技术手段,包括互斥锁.通道.计数器等,来解决并发访问和数据一致性的问题. 本 ...
- 洛谷4159 [SCOI2009] 迷路(矩阵快速幂,拆点)
题意:该有向图有 n 个节点,节点从 1至 n 编号,windy 从节点 1 出发,他必须恰好在 t 时刻到达节点 n.现在给出该有向图,你能告诉 windy 总共有多少种不同的路径吗?答案对2009 ...
- 两道题浅析PHP反序列化逃逸
两道题浅析PHP反序列化逃逸 一.介绍 反序列化逃逸的出现是因为php反序列化函数在进行反序列化操作时,并不会审核字符串中的内容,所以我们可以操纵属性值,使得反序列化提前结束. 反序列化逃逸题一般都是 ...
- 一道入门的java安全题
[XCTF]Zhuanxv 收获 java题的一般流程 HQL注入 SQL注入 看题 目录扫描 dirsearch扫目录,发现list目录: 一个登录界面,本着尽量不写sql注入题目的原则(因为太菜了 ...
- 文心一言 VS 讯飞星火 VS chatgpt (150)-- 算法导论12.2 6题
六.用go语言,考虑一棵二叉搜索树 T ,其关键字互不相同.证明:如果 T 中一个结点 x 的右子树为空,且 x 有一个后继 y ,那么 y 一定是 x 的最底层祖先,并且其左孩子也是 x 的祖先.( ...
- 一招MAX降低10倍,现在它是我的了
一.背景 性能优化是一场永无止境的旅程. 到家门店系统,作为到家核心基础服务之一,门店C端接口有着调用量高,性能要求高的特点. C端服务经过演进,核心接口先查询本地缓存,如果本地缓存没有命中,再查询R ...
- rust 过程宏
简介 Rust 编程语言里面有两种宏系统,一种是声明宏(Declarative Macros),另一种为过程宏(Procedural Macros).声明宏和过程宏是两种基本上完全不一样的宏系统,编写 ...
- 『UniApp』uni-app-打包成App
前言 大家好,我是 BNTang, 在上一节文章中,我给大家详细的介绍了如何将我开发好的项目打包为微信小程序并且发布到微信小程序商店 趁热打铁,在来一篇文章,给大家详细的介绍如何将项目打包成APP. ...
- 从零玩转Docker之docker-compose快捷部署中间件-dockercompose2
title: 从零玩转Docker之docker-compose快捷部署中间件 date: 2023-04-04 17:35:18.035 updated: 2023-05-13 23:08:09.5 ...
- electron入门之打包exe分发(五)
electron入门到入土,前面配置阿里镜像加速.为了防止我们打包下载龟速,所以需要给electron配置阿里镜像加速.参考配置阿里云镜像https://blog.csdn.net/weixin_44 ...