二分图
二分图
没有网络流不能做的二分图!(不是)
二分图最大匹配模板
最小点权覆盖也是如此。
#include<bits/stdc++.h>
using namespace std;
const int N=505;
int n,m,e,ans,bp[N],cnt[N];
bool d[N][N],vis[N];
bool dfs(int x)
{
for(int i=1;i<=m;++i)
if(d[x][i]&&!vis[i])
{
vis[i]=1;
if(!bp[i]||dfs(bp[i])){bp[i]=x;return 1;}
}
return 0;
}
int main()
{
scanf("%d %d %d",&n,&m,&e);
for(int i=1,x,y;i<=e;++i) scanf("%d %d",&x,&y),d[x][y]=1;
for(int i=1;i<=n;++i)
if(dfs(i))
{
memset(vis,0,sizeof(vis));
ans++;
}
printf("%d",ans);
}
只有DFS部分比较重要,这样写只是因为数据很小而已,数据大了还是链表储存。
acwing 棋盘覆盖
给定一个N行N列的棋盘,已知某些格子禁止放置。
求最多能往棋盘上放多少块的长度为2、宽度为1的骨牌,骨牌的边界与格线重合(骨牌占用两个格子),并且任意两张骨牌都不重叠。
输入格式
第一行包含两个整数N和t,其中t为禁止放置的格子的数量。
接下来t行每行包含两个整数x和y,表示位于第x行第y列的格子禁止放置,行列数从1开始。
输出格式
输出一个整数,表示结果。
数据范围
1≤N≤1001≤N≤100
输出样例:
8 0
输出样例:
32
#include<bits/stdc++.h>
using namespace std;
#define id(x,y) (x-1)*n+y
const int N=1e4+5;
int n,m,ans,bp[N],v[N][10];
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
bool mp[N][N],vis[N];
bool dfs(int x)
{
for(int i=1;i<=v[x][0];++i)
if(!vis[v[x][i]])
{
vis[v[x][i]]=1;
if(bp[v[x][i]]==0||dfs(bp[v[x][i]]))
{
bp[v[x][i]]=x;
return 1;
}
}
return 0;
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1,x,y;i<=m;++i) scanf("%d %d",&x,&y),mp[x][y]=1;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(!mp[i][j]&&((i+j)&1))
{
int x=id(i,j);
for(int k=0;k<4;++k)
{
int xx=i+dx[k],yy=j+dy[k];
if(xx&&xx<=n&&yy&&yy<=n&&!mp[xx][yy]) v[x][++v[x][0]]=id(xx,yy);
}
}
int S=id(n,n);
for(int i=1;i<=S;++i)
if(v[i][0]&&dfs(i))
{
memset(vis,0,sizeof(vis));
ans++;
}
printf("%d",ans);
}
acwing 机器任务
给出一些边,边连接u,v两个点,求最小点权覆盖。
#include<bits/stdc++.h>
using namespace std;
const int N=105,M=1005;
int n,m,k,bp[N];
int te,v[M],pre[M],tail[N];
bool vis[N];
inline void add(int x,int y)
{
++te;v[te]=y;pre[te]=tail[x];tail[x]=te;
}
bool dfs(int x)
{
for(int i=tail[x];i;i=pre[i])
if(!vis[v[i]])
{
vis[v[i]]=1;
if(bp[v[i]]==0||dfs(bp[v[i]]))
{
bp[v[i]]=x;
return 1;
}
}
return 0;
}
int main()
{
while(~scanf("%d",&n)&&n)
{
memset(tail,0,sizeof(tail));
memset(bp,0,sizeof(bp));
te=0;
scanf("%d %d",&m,&k);
for(int i=1,a,x,y;i<=k;++i)
{
scanf("%d %d %d",&a,&x,&y);
if(!x||!y) continue;
add(x,y);
}
int ans=0;
for(int i=1;i<=n;++i)
if(tail[i])
{
memset(vis,0,sizeof(vis));
if(dfs(i)) ans++;
}
printf("%d\n",ans);
}
}
acwing 泥泞的区域
在一块 N*M 的网格状地面上,有一些格子是泥泞的,其他格子是干净的。
现在需要用一些宽度为1、长度任意的木板把泥地盖住,同时不能盖住干净的地面。
每块木板必须覆盖若干个完整的格子,木板可以重叠。
求最少需要多少木板。
即对于每个格子,要么选一个横着覆盖它的板子,要么选一个竖着覆盖它的板子,或者两个都选。即二分图中的边。
建边的时候以向右或是向下遇到的第一个干净格子,为了保证一定有干净格子,最右和最下增加一排干净格子,这样连续的几个格子连到的点就是一样的。
acwing 马的放置
和网络流车的放置一样,网络流专题里有证明最后弄出来是一个二分染色图,对于白色格子向四周可以连边的格子连边,求最小点权覆盖。
acwing 捉迷藏
Vani和cl2在一片树林里捉迷藏。
这片树林里有N座房子,M条有向道路,组成了一张有向无环图。
树林里的树非常茂密,足以遮挡视线,但是沿着道路望去,却是视野开阔。
如果从房子A沿着路走下去能够到达B,那么在A和B里的人是能够相互望见的。
现在cl2要在这N座房子里选择K座作为藏身点,同时Vani也专挑cl2作为藏身点的房子进去寻找,为了避免被Vani看见,cl2要求这K个藏身点的任意两个之间都没有路径相连。
为了让Vani更难找到自己,cl2想知道最多能选出多少个藏身点。
输入格式
输入数据的第一行是两个整数N和M。
接下来M行,每行两个整数 x,y,表示一条从 x 到 y 的有向道路。
输出格式
输出一个整数,表示最多能选取的藏身点个数。
数据范围
N≤200,M≤30000
挑选一些点使这些点互不相连,就是指最大点权独立集,网络流专题内有详细讲解。因为N很小,所以可以直接Floyed求点与点是否相连,然后重新建图,相连的点就建边,不相连就不建,然后求最小点权覆盖,用总点数减去最小点权覆盖就是最大点权独立集。

浙公网安备 33010602011771号