先两边的点增加至n+m-res, res是原图的最大匹配,然后做一次完美匹配。新建一个图G,对于王子匹配的公主向该王子喜欢的其他公主连一条边,求强连通分量,则王子喜欢的公主并且与王子匹配的公主在同一个强联通分量里面都能与王子匹配并且最大匹配数不会减少。
实际上是王子之间交换各自匹配的公主,如图
左边是王子,右边是公主,红色是匹配边,绿色是未匹配边,蓝色是新建的图 G
因为原图不是完美匹配,有些王子公主是单身狗,无法实现上述交换
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<stdio.h>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<queue>
using namespace std;
#define inf 0x3f3f3f3f
#define eps 1e-9
#define mod 10007
#define FOR(i,s,t) for(int i = s; i < t; ++i )
#define REP(i,s,t) for( int i = s; i <= t; ++i )
#define LL unsigned long long
#define ULL unsigned long long
#define pii pair<int,int>
#define MP make_pair
#define lson id << 1 , l , m
#define rson id << 1 | 1 , m + 1 , r
#define maxn ( 2000+100 )
#define maxe ( 2000000+10 )
#define mxn 20000
vector < int > G[maxn];
int link[maxn], lx[maxn];
bool vis[maxn];
bool dfs1( int u ) {
for( int i = 0; i < G[u].size(); ++i ) {
int v = G[u][i];
if( vis[v] ) continue;
vis[v] = 1;
if( link[v] == -1 || dfs1( link[v] ) ) {
link[v] = u;
return 1;
}
}
return 0;
}
int hungry( int n ) {
memset( link, -1, sizeof( link ) );
int res = 0;
for( int i = 1; i <= n; ++i ) {
memset( vis, 0, sizeof( vis ) );
if( dfs1 ( i ) ) ++res;
}
return res;
}
int uN, vN;
int fst[maxn], vv[maxe], nxt[maxe], ec;
void add ( int u, int v ) {
vv[ec] = v;
nxt[ec] = fst[u];
fst[u] = ec++;
}
int sccno[maxn], scc_cnt, S[maxn], stop;
int pre[maxn], low[maxn], dfsc;
void dfs ( int u ) {
low[u] = pre[u] = ++dfsc;
S[stop++] = u;
for( int i = fst[u]; i != -1; i = nxt[i] ) {
int v = vv[i];
if( !pre[v] ) {
dfs( v );
low[u] = min( low[u], low[v] );
}
else if( !sccno[v] ) {
low[u] = min( low[u], pre[v] );
}
}
if( pre[u] == low[u] ) {
++scc_cnt;
while( 1 ) {
int v = S[--stop];
sccno[v] = scc_cnt;
if( v == u ) break;
}
}
}
void find_scc( int n ) {
memset( sccno, 0, sizeof( sccno ) );
memset( pre, 0, sizeof( pre ) );
scc_cnt = dfsc = 0;
stop = 0;
for( int i = 1; i <= n; ++i ) {
if( !pre[i] ) dfs( i );
}
}
vector < int > ans;
int readint () {
char c;
int res = 0;
while( ( c = getchar() ) && ( c < '0' || c > '9' ) ) ;
res = c - '0';
while( ( c = getchar() ) && ( '0' <= c && c <= '9' ) )
res = res * 10 + c - '0';
return res;
}
void out ( int x ) {
if( x > 9 )
out( x / 10 );
putchar( x % 10 + '0' );
}
int main () {
int T, cas = 1;
scanf("%d", &T ) ;
while( T-- ) {
int k, n, m, u, v;
scanf("%d%d", &n, &m ) ;
for( int i = 1; i <= n;++i ) {
G[i].clear();
k = readint();
for( int j = 0; j < k; ++j ) {
v = readint();
G[i].push_back( v );
}
}
int res = hungry( n );
vN = uN = n + m - res;
for( int i = n+1; i <= uN; ++i ) {
G[i].clear();
for( int j = 1; j <= vN; ++j )
G[i].push_back( j );
}
for( int i = 1; i <= uN; ++i )
for( int j = m + 1;j <= vN; ++j )
G[i].push_back( j );
hungry( uN );
memset( lx, -1, sizeof( lx ) );
for( int i = 1; i <= vN; ++i )
if( link[i] != -1 )
lx[link[i]] = i;
memset( fst, -1, sizeof( fst ) );
ec = 0;
for( int i = 1; i <= uN; ++i )
for( int j = 0; j < G[i].size(); ++j )
if( G[i][j] != lx[i] ) add( lx[i], G[i][j] );
find_scc( vN );
printf("Case #%d:\n", cas++ );
for( int i = 1; i <= n; ++i ) {
ans.clear();
for( int j = 0; j < G[i].size(); ++j ) {
int v = G[i][j];
if( v > m ) continue;
if( sccno[v] == sccno[lx[i]] )
ans.push_back( v );
}
sort( ans.begin(), ans.end() );
out( ans.size() );
for( int i =0; i < (int)ans.size(); ++i )
{
putchar( ' ' );
out( ans[i] );
}
printf("\n" );
}
}
}