算法标签:二分图最大独立集
二分图各种类型题目数不胜数,这一题便是一个二分图的最大独立集问题
前置芝士
先简短介绍一下匈牙利算法
其实匈牙利算法就是一个妥协的过程,即若一点
A
A
A于另一点
B
B
B相连,而该边已有匹配
C
C
C,则尽可能的让
C
C
C去寻求其他点进行匹配,然后让
A
,
B
A,B
A,B完成匹配(不太地道的感觉)
核心代码:
inline bool dfs(int x)
{
for (int i=head[x];i;i=edge[i].next)
{
int y=edge[i].to;
if (vis[y]==false)
{
vis[y]=true;
if (match[y]==0||dfs(match[y])==true)
{
match[y]=x;
return true;
}
}
}
}
二分图最大独立集
定义:
对于一张无向图
G
(
V
,
E
)
G(V,E)
G(V,E),选出一个
V
V
V的子集
S
S
S,使得
S
S
S中任意两点之间都没有边,且
S
S
S是最大这样的子集。
求解方法:
二分图的最大独立集的点的数量=总点数N-最大匹配而最大匹配用匈牙利即可求得
简单的证明:要使得独立集越大,那么删掉的点就要越少,同时话要保证删掉的点覆盖到所有边,所以删掉的因该是最小点覆盖。
推荐题目:
- Example1.[LGOJ] P5030长脖子鹿防置
- Example2.[LGOJ]P3355 骑士共存问题
好的,那么介绍完最大独立集,讲回我们这道题
这题是一个非常典型的二分图最大独立集问题,发现每两条均均是不重合的,于是想到直接将相交的两条线段连接,得到的一点是一个二分图(同边的(即同方向)没有边相连,符合二分图定义)
于是呢我们发现题目所求是"选出尽量多的线段使得这些线段两两没有交点",那么在二分图中间也就是选出尽量多的点使得他们之间没有连边,通过这个建模,我们就将这个题完美地转化成了一个裸的二分图最大独立集问题了,根据
二分图最大独立集=点的个数 − - −最大匹配于是乎这题就可以求解了
最后再讲一下怎么判断相交的问题
- 判断线段方向即看横纵做坐标哪个相等即可,若 X 1 = X 2 X1=X2 X1=X2则方向为竖,若 Y 1 = Y 2 Y1=Y2 Y1=Y2则为横
- 对每个坐标进行排序,于是我们得到:
inline bool intersect(node2 x,node2 y) //x为竖边,y为横边,且横纵坐标均为从小到大
{
if (x.a.x>=y.a.x&&x.a.x<=y.b.x&&x.a.y<=y.a.y&&x.a.y>=y.b.y)
{
return 1;
}
return 0;
}
即竖边的 x x x坐标位于横边的两个端点的 x x x坐标之间并且横边的 y y y坐标位于竖边的两个端点的 y y y坐标之间
ok,于是我们便愉快地得到了代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000005;
int n,cnt,tot,ans;
int head[maxn],vis[maxn],match[maxn];
struct node
{
int x,y;
};
struct node2
{
int dir;
node a,b;
}seg[maxn];
struct node3
{
int from,to,next;
}edge[maxn];
inline int add(int x,int y)
{
edge[++tot].next=head[x];
edge[tot].from=x;
edge[tot].to=y;
head[x]=tot;
}
inline int read()
{
int s=0,f=1;
char c=getchar();
while (c<'0'||c>'9')
{
if (c=='-')
{
f=-1;
}
c=getchar();
}
while (c>='0'&&c<='9')
{
s=s*10+c-48;
c=getchar();
}
return s*f;
}
inline bool cmp(node2 x,node2 y)
{
return x.dir<y.dir;
}
inline bool intersect(node2 x,node2 y)
{
if (x.a.x>=y.a.x&&x.a.x<=y.b.x&&y.a.y>=x.a.y&&y.a.y<=x.b.y)
{
return 1;
}
return 0;
}
inline bool dfs(int x)
{
for (int i=head[x];i;i=edge[i].next)
{
int y=edge[i].to;
if (vis[y]==false)
{
vis[y]=true;
if (match[y]==0||dfs(match[y])==true)
{
match[y]=x;
return true;
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
n=read();
for (int i=1;i<=n;i++)
{
seg[i].a.x=read();
seg[i].a.y=read();
seg[i].b.x=read();
seg[i].b.y=read();
if (seg[i].a.x==seg[i].b.x)
{
seg[i].dir=1;
cnt++;
if (seg[i].a.y>seg[i].b.y)
{
swap(seg[i].a.y,seg[i].b.y);
}
}
if (seg[i].a.y==seg[i].b.y)
{
seg[i].dir=2;
if (seg[i].a.x>seg[i].b.x)
{
swap(seg[i].a.x,seg[i].b.x);
}
}
}
sort(seg+1,seg+n+1,cmp);
for (int i=1;i<=cnt;i++)
{
for (int j=cnt+1;j<=n;j++)
{
if (intersect(seg[i],seg[j]))
{
add(i,j);
}
}
}
for (int i=1;i<=cnt;i++)
{
memset(vis,0,sizeof(vis));
if (dfs(i))
{
ans++;
}
}
cout<<n-ans<<endl;
return 0;
}