题意:n个王子,m个公主,配对。每个王子可以在给出的ki个公主中选一个结婚。。
求最大的配对情况下,配对的状态。
题解:最后的answer 肯定是一些公主之间的连通的状态。
二分匹配找到与王子配对的公主。
再对公主对应的王子对应的ki个公主建边,形成的连通图既是对应某个王子的能取的公主
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <string>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <cmath>
using namespace std;
#include <queue>
#include <stack>
#include <vector>
#include <deque>
#include <set>
#include <map>
#define cler(arr, val) memset(arr, val, sizeof(arr))
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define IN freopen ("in.txt" , "r" , stdin);
#define OUT freopen ("out.txt" , "w" , stdout);
typedef long long LL;
const int MAXN = 1011;
const int MAXM = 610;
const int INF = 0x3f3f3f3f;
const LL mod = 2147483647;
const double eps= 1e-8;
const double pi=acos(-1.0);
#define lson l,m, rt<<1
#define rson m+1,r,rt<<1|1
const int N = 21001;
const int M = 200010;
int n, m;//n m 为点数和边数
struct Edge{
int from, to, nex;
bool sign;//是否为桥
}edge[M];
int head[N], edgenum;
void add(int u, int v){//边的起点和终点
Edge E={u, v, head[u], false};
edge[edgenum] = E;
head[u] = edgenum++;
}
int DFN[N], Low[N], Stack[N], top, Time; //Low[u]是点集{u点及以u点为根的子树} 中(所有反向弧)能指向的(离根最近的祖先v) 的DFN[v]值(即v点时间戳)
int taj;//连通分支标号,从1开始
int Belong[N];//Belong[i] 表示i点属于的连通分支
int bridge;
bool Instack[N];
vector<int> bcc[N]; //标号从1开始
void tarjan(int u ,int fa)
{
DFN[u] = Low[u] = ++ Time ;
Stack[top ++ ] = u ;
Instack[u] = 1 ;
for (int i = head[u] ; ~i ; i = edge[i].nex )
{
int v = edge[i].to;
if(DFN[v] == -1)
{
tarjan(v , u);
Low[u] = min(Low[u] ,Low[v]) ;
if(DFN[u] < Low[v])
{
edge[i].sign =edge[i^1].sign = 1;//为割桥
}
}
else if(Instack[v])
{
Low[u] = min(Low[u] ,DFN[v]) ;
}
}
if(Low[u] == DFN[u])
{
int now;
taj ++ ;
bcc[taj].clear();
do{
now = Stack[-- top] ;
Instack[now] = 0 ;
Belong [now] = taj ;
bcc[taj].push_back(now);
}while(now != u) ;
}
}
void tarjan_init(int all){
memset(DFN, -1, sizeof(DFN));
memset(Instack, 0, sizeof(Instack));
top = Time = taj = 0;
for(int i=1;i<=all;i++)
if(DFN[i]==-1 )
tarjan(i, -1); //注意开始点标!!!
}
vector<int>G[N];
int uN,vN;//u,v数目
int g[MAXN][MAXN];
int linker[MAXN],link[MAXN];
bool used[MAXN];
bool dfs(int u)//从左边开始找增广路径
{
int v;
for(v=1;v<=vN;v++)//这个顶点编号从0开始,若要从1开始需要修改
if(g[u][v]&&!used[v])
{
used[v]=true;
if(linker[v]==-1||dfs(linker[v]))
{//找增广路,反向
linker[v]=u;
link[u]=v;
return true;
}
}
return false;//这个不要忘了,经常忘记这句
}
int hungary()
{
int res=0;
int u;
memset(linker,-1,sizeof(linker));
memset(link,-1,sizeof(link));
for(u=1;u<=uN;u++)
{
memset(used,0,sizeof(used));
if(dfs(u)) res++;
}
return res;
}
void init()
{
memset(head, -1, sizeof(head));
edgenum=0;
cler(g,0);
}
int main()
{
int cas=1,t;
cin>>t;
while(t--)
{
scanf("%d%d",&n,&m);
init();
int a,b,x;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
for(int j=0;j<x;j++)
{
scanf("%d",&a);
g[i][a]=1;
}
}
uN=n,vN=m;
x=hungary();
int num=n+m-x;
for(int i=n+1;i<=num;i++)
for(int j=1;j<=num;j++)
g[i][j]=1;
for(int i=m+1;i<=num;i++)
for(int j=1;j<=num;j++)
g[j][i]=1;
uN=vN=num;
hungary();
for(int i=1;i<=num;i++)
{
for(int j=1;j<=num;j++)
{
if(link[i]!=j&&g[i][j])
add(link[i],j);
}
}
tarjan_init(num);
for(int i = 0; i <=n; i++)
G[i].clear();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(g[i][j]&&Belong[link[i]]==Belong[j])
G[i].push_back(j);
printf("Case #%d:\n",cas++);
for(int i=1;i<=n;i++)
{
printf("%d",G[i].size());
for(int j=0;j<G[i].size();j++)
printf(" %d",G[i][j]);
puts("");
}
}
return 0;
}