话不多说,只是贴板子
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#define LL long long
using namespace std;
inline void du(int &d)
{
char t=getchar(); bool Mark=false;
for(;t<'0'||t>'9';t=getchar())if(t=='-')Mark=!Mark;
for(d=0;t>='0'&&t<='9';t=getchar())d=d*10+t-'0';
if(Mark)d=-d;
}
// 本页代码 POJ 1274
// 二分图匹配模板
const int maxn = 600;
const int maxe = 100000;
int edge_cnt = 0; // 链式前向星 的依赖
int Last[maxe], Next[maxe], Des[maxe], Len[maxe]; // 链式前向星存图, len非必要
int link[maxn]; // 右边的点与左边匹配点的编号
int visited[maxn]; // 最新访问的轮数/时间戳, 用于判断在某一次增广是是否经过了该点(右边的点)
int times; //时间戳
int n, m; // n 为左边节点数, m为右边节点数
void addedge(int x, int y, int value);
bool Find(int u);
int Hungarian()
{
for(int i = 1; i <= m; i ++)link[i] = -1; // -1 表示还没有匹配上
memset(visited,0,sizeof(visited)); // 清空上一组数据的时间戳(这个不清空也没关系)
times = 0; // 时间戳归零 (如果上面不清空,那么这个也就不归零)
int ans = 0;
for(int i = 1; i <= n; i ++){ times++; if(Find(i))ans++; } // 依次对左边的每一个点开始进行增广, 成功就ans++
return ans;
}
void solve()
{
int x, y ,s , i , j ;
// 对于每组数据, 清空前面数据的残留
memset(Last, 0, sizeof(Last) );
memset(Next, 0 , sizeof(Next) );
memset(Des, 0 , sizeof(Des) );
edge_cnt = 0;
for(i = 1; i <= n; i++)
{
du(s);
for(j = 1; j <= s; j++)
{
du(x);
addedge(i, x, 1); // 建边, 左边第i 个点 与 右边第x个点相连
}
}
int ans = Hungarian();
cout << ans << endl;
}
int main()
{
while(scanf("%d%d",&n, &m) != EOF )solve();
}
bool Find(int u)
{
for( int i = Last[u], v; i != 0; i = Next[i] )
{
v = Des[i];
if(visited[v] == times)continue; // times是当前是第几次尝试增广,时间戳
visited[v] = times; // 更新时间戳
if(link[v] == -1 || Find(link[v]) ) // v是尚未匹配的点, 或者 link[v] 能成功增广
{
link[v] = u;
return true;
}
}
return false;
}
void addedge(int x, int y, int value)
{
edge_cnt++;
Next[edge_cnt] = Last[x]; Last[x] = edge_cnt;
Des[edge_cnt] = y; Len[edge_cnt] = value;
}