https://vjudge.net/problem/UVA-12083(uva12083 点击打开链接)
思路:二分图的最大独立集(二分图中任选两点之间,都不会有边相连)。
分析:首先,二分图肯定不能有边将同一侧的点相连。他们只要满足四个条件中的一个就不会产生爱意,相反,如果四个条件都不满足,就会产生爱意。每个人要么男,要么女,这样同侧就肯定不会有边相连,然后左右两边建边。如果其他三个条件都不满足,就建一条无向边。所以这个问题就变成了选择尽量多的点,满足任意一条边的两个点都不能同时被选到。这就是最大独立集。
那么怎么解这个最大独立集?其实它是和最小点覆盖互补的。互补就是指,数目是总点数减最小覆盖,解集只要把最小覆盖里的“已选点”和“未选点”互换即可。
为什么是互补?看两者的定义是什么。
(1)最小覆盖集:对于每条边,至少有一个点被选中。
(2)最大独立集:对于每条边,至少有一个点不被选中。
每个最小覆盖集都和一个唯一的最大独立集互补,每个最大独立集也都和一个唯一的最小覆盖集互补。
所以,综上:
最大独立集:选择尽量多的点,满足任意一条边的两个端点不被同时选中。
解集:与最小覆盖集互补。最大独立集点数 = 总点数 - 最小覆盖集点数 = 总点数 - 最大匹配数。
代码:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 500 + 10;
struct node
{
int height;
char sex;
string music;
string sport;
}person[maxn];
int t;
int line[maxn];
bool used[maxn];
bool judge(const node a,const node b)
{
if(abs(a.height - b.height) <= 40 && a.sex != b.sex && a.music == b.music && a.sport != b.sport)
return true;
return false;
}
//板子:
bool dfs(int x)
{
//右边的点
for(int i = 1;i <= t;i++)
{
if(i != x && used[i] == false)
{
if(judge(person[i],person[x]))
{
used[i] = 1;
if(line[i] == 0 || dfs(line[i]))
{
line[i] = x;
return true;
}
}
}
}
return false;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&t);
for(int i = 1;i <= t;i++)
{
cin >> person[i].height >> person[i].sex;
cin >> person[i].music >> person[i].sport;
}
memset(line,0,sizeof(line));
int ans = 0;
for(int i = 1;i <= t;i++)//左边的点
{
memset(used,0,sizeof(used));
if(dfs(i))
ans++;
}
printf("%d\n",t - ans/2);
}
}
模板:
#include
#include
#include
using namespace std;
const int N=1001;
int n1,n2,k;
//n1,n2为二分图的顶点集,其中x∈n1,y∈n2
int map[N][N],vis[N],link[N];
//link记录n2中的点y在n1中所匹配的x点的编号
int find(int x)
{
int i;
for(i=1;i<=n2;i++)
{
if(map[x][i]&&!vis[i])//x->i有边,且节点i未被搜索
{
vis[i]=1;//标记节点已被搜索
//如果i不属于前一个匹配M或被i匹配到的节点可以寻找到增广路
if(link[i]==0||find(link[i]))
{
link[i]=x;//更新
return 1;//匹配成功
}
}
}
return 0;
}
int main()
{
int i,x,y,s=0;
scanf("%d%d%d",&n1,&n2,&k);
for(i=0;iscanf("%d%d",&x,&y);
map[x][y]=1;
}
for(i=1;i<=n1;i++)
{
memset(vis,0,sizeof(vis));
if(find(i))
s++;
}
printf("%d\n",s);
return 0;
}