题意是删掉一些点使得两个集合没有整除关系,并且保证删的点最少。
即是求最小覆盖点集,即是求最大二分匹配。
二分匹配的几个重要定理:
最小覆盖点数 = 最大匹配数
最大独立集 = 点数 - 最大匹配数
最小路径覆盖 = 点数 - 最大匹配数
最小覆盖点数:假如选了一个点就相当于覆盖了以它为端点的所有边,你需要选择最少的点来覆盖所有的边。
最小路径覆盖:在一个N*N的有向图中,路径覆盖就是在图中找一些路经,使之覆盖了图中的所有顶点, 且任何一个顶点有且只有一条路径与之关联;(如果把这些路径中的每条路径从它的起始点走到它的终点那么恰好可以经过图中的每个顶点一次且仅一次);如果不考虑图中存在回路,那么每每条路径就是一个弱连通子集.
独立集:独立集:图中任意两个顶点都不相连的顶点集合。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100+10;
vector<int>g[N];
bool vis[N];
int from[N];
map<LL,int>mymap;
bool match(int x)//here
{
for(int i=0;i<g[x].size();i++)
{
int v = g[x][i];
if(vis[v]) continue;
vis[v] = true;
if(from[v] == -1 || match(from[v]))
{
from[v] =x;
return true;
}
}
return false;
}
int main()
{
int T;
scanf("%d",&T);
for(int cas = 1;cas<=T;cas++)
{
int n;
scanf("%d",&n);
for(int i=0;i<=n;i++) g[i].clear();
mymap.clear();
memset(from,-1,sizeof(from));
for(int i=1;i<=n;i++)
{
LL x;
scanf("%lld",&x);
mymap[i] = x;
}
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
LL x ;
scanf("%lld",&x);
for(map<LL,int>::iterator it = mymap.begin();it!=mymap.end();it++)
{
if(x%(it->second)==0)
{
g[it->first].push_back(i);
}
}
}
int cnt = 0;
for(map<LL,int>::iterator it = mymap.begin();it!=mymap.end();it++)//here
{
memset(vis,false,sizeof(vis));
if(match(it->first))
cnt++;
}
printf("Case %d: %d\n",cas,cnt);
}
return 0;
}