题意:有 T(T≤20) 组数据。Bob在与他的 n−1(2≤n≤10) 个同伴交换糖纸,一共有 m(5≤m≤25) 种糖纸。Bob希望能和同伴交换使得手上的糖纸数尽量多。他的同伴只会用手上的重复的交换手上没有的,并且他的同伴们之间不会产生交换。求出Bob能拥有的最大糖纸种数。
分析:
最大流。
①:源点(Bob)->m种糖纸:cap是Bob持有贴纸数量。
②:所有同伴->m种糖纸:持有的该贴纸数量>=2,连一条边,cap是 糖纸数量 - 1。(只会换重复的贴纸)。
③:m种糖纸->所有同伴:没这种糖纸,连一条边,cap=1,。(只接受自己没有的贴纸)
④:m种糖纸->汇点:cap=1。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<cctype>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<iomanip>
#include<sstream>
#include<limits>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
#define INF 0x7fffffff
const int maxn = 1e5+10;
struct Edge{
int to , cap,rev; // rev反向边
};
vector<Edge>G[maxn];
int level[maxn],iter[maxn]; // 顶点到源点的标号 当前弧
void add_edge(int from,int to,int cap) //cap容量
{
G[from].push_back((Edge){to,cap,G[to].size()});
G[to].push_back((Edge){from,0,G[from].size()-1 });
}
void bfs(int s) // 分层
{
memset(level,-1,sizeof(level));
queue<int>que;
level[s] = 0;
que.push(s);
while(!que.empty())
{
int v = que.front();que.pop();
for(int i = 0; i < G[v].size(); i++)
{
Edge &e = G[v][i];
if (e.cap>0 && level[e.to] < 0)
{
level[e.to] = level[v]+1;
que.push(e.to);
}
}
}
}
int dfs(int v, int t, int f) //寻增广路
{
if (v==t) return f;
for(int &i = iter[v]; i < G[v].size(); i++)
{
Edge &e = G[v][i];
if (e.cap > 0 && level[v] < level[e.to])
{
int d = dfs(e.to , t, min(f,e.cap));
if (d > 0)
{
e.cap -= d;
G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
int max_flow(int s, int t) // s到t最大流
{
int flow = 0;
for(;;)
{
bfs(s);
if (level[t] < 0) return flow;
memset(iter,0,sizeof(iter));
int f;
while((f = dfs(s,t,INF)) > 0) { flow += f;}
}
}
int num[maxn];
int main(){
#ifdef LOCAL
freopen("C:\\Users\\lanjiaming\\Desktop\\acm\\in.txt","r",stdin);
//freopen("output.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
int T,n,m,kase = 0;
cin>>T;
while(T--)
{
cin>>n>>m;
int s = 0, t = n+m+1;
for(int i= 0; i <= n+m+1; i++) G[i].clear();
int k;
cin>>k;
memset(num,0,sizeof(num));
while(k--)
{
int x;
cin>>x;
num[x]++;
}
for(int i = 1; i <= m; i++) add_edge(s,i,num[i]);
for(int i = 1; i < n; i++)
{
memset(num,0,sizeof(num));
cin>>k;
while(k--)
{
int x;
cin>>x;
num[x]++;
}
for(int j = 1; j <= m; j++)
{
if (num[j] >= 2) add_edge(m+i,j,num[j]-1);
else if(num[j] == 0) add_edge(j,m+i,1);
}
}
for(int i = 1; i <= m; i++) add_edge(i,t,1);
cout<<"Case #"<<++kase<<": ";
cout<<max_flow(s,t)<<endl;
}
return 0;
}