//hdu3517
//最大独立集
//此题求独立集中的关键点
//对于某点,如果未匹配(cx[i]==-1,cy[i]==-1),肯定在关键点中
//对于匹配中的点,若此点不是匹配中的关键点,那么删除此点最大匹配数不会改变,
//|V|减小,独立集变小,不能删。
//那么此点为独立集中的关键点
//(如果此点是二分图中的关键点,那么删除此点,最大匹配数减小,那么|V|-|M|增大,可以删。)
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N = 210;
const int INF=0x7fffffff;
#define clr(a) memset(a,0,sizeof(a));
int bmap[N][N];//每次记得清空
//对于两边各50000个点,200000条边的二分图最大匹配可以在1s内出解,效果很好:)
int X[N],Y[N];
struct HK{//用HK算法求解二分最大匹配问题(时间复杂度为sqrt(v)*e)
int nx,ny,dis;
int cx[N];//cx[i]表示左集合i顶点所匹配的右集合的顶点序号
int cy[N]; //cy[i]表示右集合i顶点所匹配的左集合的顶点序号
int dx[N];
int dy[N];
int Q[N];
bool bmask[N];
bool searchpath(){//极大最短增广路集
int front,rear;
front=rear=0;//初始化队列
dis=INF;
memset(dx, -1, sizeof(dx));
memset(dy, -1, sizeof(dy));
for(int i=1;i<=nx;i++){
if(cx[i]==-1)//左边未匹配
{
Q[rear++]=i;//将左边节点放入队列
dx[i]=0;//分层 所有潜在起点构成0层
}
}
while(front<rear){
int u=Q[front++];//u都是未匹配的
if(dx[u]>dis)break;
for(int v=1;v<=ny;v++){
if(bmap[u][v]&&dy[v]==-1){//相邻未分层构成i+1层。
dy[v]=dx[u]+1;//v对应的距离 为u对应距离加1
if(cy[v]==-1)dis=dy[v];//右侧未匹配,更新最短距离
else{
dx[cy[v]]=dy[v]+1;//右侧已经匹配,根据已匹配边回溯。分层
Q[rear++]=cy[v];//左侧已匹配点入队。
}
}
}
}
return dis!=INF;
}
int findpath(int u){//只搜索极大最短增广路集中的边
for(int v=1;v<=ny;v++){
if(!bmask[v]&&bmap[u][v]&&dy[v]==dx[u]+1){//反向搜索的过程
bmask[v]=1;
if(cy[v]!=-1&&dy[v]==dis)continue;
if(cy[v]==-1||findpath(cy[v])){
cy[v]=u;
cx[u]=v;
return 1;
}
}
}
return 0;
}
int Max_match(){
int res=0;
memset(cx,-1,sizeof(cx));
memset(cy,-1,sizeof(cy));
while(searchpath()){
memset(bmask, 0, sizeof(bmask));
for(int i=1;i<=nx;i++){
if(cx[i]==-1){
res+=findpath(i);
}
}
}
return res;
}
};
HK hk;
//调用的时候确定hk.nx,hk.ny,然后直接调用Max_match()就行
struct P{
int like;
int disnum;
int dis[N];
}P[N];
int a,b;
void input(){
int x,n;
scanf("%d%d%d",&a,&b,&n);
for(int i=1;i<=n;i++){
scanf("%d",&x);
if(x<=a){//只会反对对方党派的,而且多个人支持相同的,相当于满意的人数累加。拆点
X[++hk.nx]=i;
}
else{
Y[++hk.ny]=i;
}
P[i].like=x;
scanf("%d",&P[i].disnum);
for(int j=0;j<P[i].disnum;j++){
scanf("%d",&P[i].dis[j]);
}
}
for(int i=1;i<=hk.nx;i++){
for(int j=1;j<=hk.ny;j++){
int L=X[i],R=Y[j];
for(int k=0;k<P[R].disnum;k++){
if(P[L].like==P[R].dis[k]){
bmap[i][j]=1;break;
}
}
if(!bmap[i][j]){
for(int k=0;k<P[L].disnum;k++){
if(P[R].like==P[L].dis[k]){
bmap[i][j]=1;break;
}
}
}
}
}
}
int used[N];
int pointX[N],pointY[N];
int delX,delY;
bool findY(int u){
for(int v=1;v<=hk.ny;v++){
if(v!=delY&&bmap[u][v]&&!used[v]){
used[v]=1;
if(hk.cy[v]==-1||findY(hk.cy[v])){
return 1;
}
}
}
return 0;
}
bool findX(int v){
for(int u=1;u<=hk.nx;u++){
if(u!=delX&&bmap[u][v]&&!used[u]){
used[u]=1;
if(hk.cx[u]==-1||findX(hk.cx[u])){
return 1;
}
}
}
return 0;
}
void solve(){
for(int i=1;i<=hk.nx;i++){
if(hk.cx[i]==-1){
pointX[i]=1;
continue;//关键点在一定在匹配M中
}
delY=hk.cx[i];
clr(used);
if(findY(i))pointY[delY]=1;
}
for(int i=1;i<=hk.ny;i++){
if(hk.cy[i]==-1){
pointY[i]=1;
continue;
}
delX=hk.cy[i];
clr(used);
if(findX(i))pointX[delX]=1;
}
}
int pro[N];
int main(){
int T;
scanf("%d",&T);
while(T--){
memset(bmap,0,sizeof(bmap));
clr(pro);
hk.nx=hk.ny=0;
clr(pointX);clr(pointY);
input();//建图
int ans=hk.Max_match();
solve();
printf("%d\n",hk.nx+hk.ny-ans);
int cnt=0;
for(int i=1;i<=hk.nx;i++){
if(pointX[i]==1){
pro[P[X[i]].like]=1;
}
}
for(int i=1;i<=hk.ny;i++){
if(pointY[i]==1){
pro[P[Y[i]].like]=1;
}
}
for(int i=1;i<=a+b;i++){
if(pro[i]==1)++cnt;
}
printf("%d",cnt);
for(int i=1;i<=a+b;i++){
if(pro[i]==1)printf(" %d",i);
}
printf("\n");
}
return 0;
}
hdu 3517 Adopt or not(二分图最大独立集关键点)
最新推荐文章于 2020-05-10 12:34:21 发布