题目链接:点击打开链接
题意:白书P370
思路:
因为问的是最后贴纸总数,那么就设最后的贴纸总数是网络流的答案。
首先我们模拟贴纸的流动过程:
Bob 的 某种贴纸a -> 给一个没有a贴纸的人Peo -> 还给Bob一个Peo的某张重复贴纸 -> 这张贴纸可以算作答案了
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include <queue>
using namespace std;
#define ll int
const int MAXN = 100010;//点数的最大值
const int MAXM = 400010;//边数的最大值
#define N MAXN
#define M MAXM
const int INF = 0x3f3f3f3f;
#define inf INF
struct Edge{
ll from, to, cap, nex;
}edge[M*2];//注意这个一定要够大 不然会re 还有反向弧
ll head[N], edgenum;
void add(ll u, ll v, ll cap, ll rw = 0){ //如果是有向边则:add(u,v,cap); 如果是无向边则:add(u,v,cap,cap);
Edge E = { u, v, cap, head[u]};
edge[ edgenum ] = E;
head[u] = edgenum ++;
Edge E2= { v, u, rw, head[v]};
edge[ edgenum ] = E2;
head[v] = edgenum ++;
}
ll sign[N];
bool BFS(ll from, ll to){
memset(sign, -1, sizeof(sign));
sign[from] = 0;
queue<ll>q;
q.push(from);
while( !q.empty() ){
ll u = q.front(); q.pop();
for(ll i = head[u]; i!=-1; i = edge[i].nex)
{
ll v = edge[i].to;
if(sign[v]==-1 && edge[i].cap)
{
sign[v] = sign[u] + 1, q.push(v);
if(sign[to] != -1)return true;
}
}
}
return false;
}
ll Stack[N], top, cur[N];
ll Dinic(ll from, ll to){
ll ans = 0;
while( BFS(from, to) )
{
memcpy(cur, head, sizeof(head));
ll u = from; top = 0;
while(1)
{
if(u == to)
{
ll flow = inf, loc;//loc 表示 Stack 中 cap 最小的边
for(ll i = 0; i < top; i++)
if(flow > edge[ Stack[i] ].cap)
{
flow = edge[Stack[i]].cap;
loc = i;
}
for(ll i = 0; i < top; i++)
{
edge[ Stack[i] ].cap -= flow;
edge[Stack[i]^1].cap += flow;
}
ans += flow;
top = loc;
u = edge[Stack[top]].from;
}
for(ll i = cur[u]; i!=-1; cur[u] = i = edge[i].nex)//cur[u] 表示u所在能增广的边的下标
if(edge[i].cap && (sign[u] + 1 == sign[ edge[i].to ]))break;
if(cur[u] != -1)
{
Stack[top++] = cur[u];
u = edge[ cur[u] ].to;
}
else
{
if( top == 0 )break;
sign[u] = -1;
u = edge[ Stack[--top] ].from;
}
}
}
return ans;
}
void init(){memset(head,-1,sizeof head); edgenum = 0;}
int n, m, k, s, t;
int a[30];
void go(){
int x;
scanf("%d", &x);
memset(a, 0, sizeof a);
while(x--){
int y; scanf("%d",&y); a[y]++;
}
}
void solve(){
init();
scanf("%d %d", &n, &m);
s = 1, t = n+m+1;
go();
for(int i = 1; i <= m; i++) if(a[i])
add(s, n+i, a[i]);
for(int i = 2; i <= n; i++)
{
go();
for(int j = 1; j <= m; j++)
if(a[j]-1 > 0)
add(i, n+j, a[j] -1);
else if(a[j]==0)
add(n+j, i, 1);
}
for(int i = 1; i <= m; i++)
add(n+i, t, 1);
printf("%d\n", Dinic(s,t));
}
int main(){
int T, Cas = 1; scanf("%d",&T);
while(T--){
printf("Case #%d: ", Cas++);
solve();
}
return 0;
}