Magic Potion

题目

There are n heroes and m monsters living in an island. The monsters became very vicious these days, so the heroes decided to diminish the monsters in the island. However, the i i i-th hero can only kill one monster belonging to the set M i M_{i} Mi. Joe, the strategist, has k k k bottles of magic potion, each of which can buff one hero’s power and let him be able to kill one more monster. Since the potion is very powerful, a hero can only take at most one bottle of potion.

Please help Joe find out the maximum number of monsters that can be killed by the heroes if he uses the optimal strategy.

Input

The first line contains three integers n , m , k ( 1 ≤ n , m , k ≤ 500 ) n, m, k (1 ≤ n, m, k ≤ 500) n,m,k(1n,m,k500) — the number of heroes, the number of monsters and the number of bottles of potion.

Each of the next n n n lines contains one integer t i t_{i} ti, the size of M i M_{i} Mi, and the following t i ti ti integers M i , j ( 1 ≤ j ≤ t i ) M_{i,j} (1 ≤ j ≤ ti) Mi,j(1jti), the indices (1-based) of monsters that can be killed by the i i i-th hero ( 1 ≤ t i ≤ m , 1 ≤ M i , j ≤ m ) (1 ≤ t_{i} ≤ m, 1 ≤ M_{i,j} ≤ m) (1tim,1Mi,jm).

Output

Print the maximum number of monsters that can be killed by the heroes.

Examples
Sample input

3 5 2
4 1 2 3 5
2 2 5
2 1 2

Sample output

4

Sample input

5 10 2
2 3 10
5 1 3 4 6 10
5 3 4 6 8 9
3 1 9 10
5 1 3 6 7 10

Sample output

7

思路

经典网络流,入门题= =
题意: 每个奥特曼可以打败一个怪兽,但是有k瓶药,吃了一口药可以多打败一只怪兽,每个奥特曼最多吃一口药,给出每个怪兽可以被那些奥特曼打败,求最多可以打败多少只怪兽~

解题思路:网络流(妙不可言),其中的最大流,流量可以表示打败多少只怪兽,我们按照题意一步一步推导

  1. 首先建立一个源点和汇点,将源点和奥特曼,奥特曼和怪兽,怪兽和汇点连起来
  2. 为了限制一个奥特曼原有基础上只能打败一个怪兽,我们令源点到每一个奥特曼路径的最大流量设置为1
  3. 因为每个怪兽可以被那些奥特曼打败,但只能被打败一次,所以令怪兽到汇点的路径最大流量设置为1,奥特曼到怪兽之前必须有一条流路(路径 )就可以满足,所以让其流量为无穷(大于1不影响一个怪兽只能被打败一次即可)
  4. 然后又因为有k瓶药,可以增加流量,所以首先开一个点p,让汇点连到点p,设置最大流量为k就可以保证最多k瓶药,然后p点与每一个奥特曼相连,表示每个奥特曼都可以吃药,设置最大流量为1,表示一个奥特曼最多吃一口药,然后就建图完成啦~

如下面例子画图
n=2,m=2,k
2 1 2
2 1 3
其中 1 2表示奥特曼 3 4表示怪兽,最大流即所能打败的最多怪兽
在这里插入图片描述

#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
typedef long long ll;
using namespace std;
const int mx=2e5+10;
const long long inf=1e9;
struct node{
	int next,to,w;
}G[mx];
int n,m,k,s,t,tot;
int head[mx],d[mx],cur[mx];

void init()
{
	s=0,t=n+m+1,tot=0;
	memset(head,-1,sizeof(head));
}

void add(int u,int v,int w)
{
	G[tot].to=v,G[tot].w=w;
	G[tot].next=head[u],head[u]=tot++;
	G[tot].to=u,G[tot].w=0;
	G[tot].next=head[v],head[v]=tot++;
}

int dfs(int u,int maxflow)
{
	if(u==t)
	return maxflow;
	for(int& i=cur[u];i!=-1;i=G[i].next)
	{   //注意这里的&符号,这样i增加的同时也能改变cur[u]的值,达到记录当前弧的目的 
		int v=G[i].to;
		if(d[v]==d[u]+1&&G[i].w>0)
		{
			int flow=dfs(v,min(maxflow,G[i].w));
			if(flow)
			{
			  G[i].w-=flow;
			  G[i^1].w+=flow;
			  return flow;
		    }
		}
	}
	return 0;
}

int bfs()
{
	memset(d,0,sizeof(d));
	queue<int>q;
	d[s]=1;
	q.push(s);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		for(int i=head[u];i!=-1;i=G[i].next)
		{
			int v=G[i].to;
			if(d[v]==0&&G[i].w>0)
			{
				d[v]=d[u]+1;
				q.push(v);
			}
		}
	}
	if(d[t]>0)
	return 1;
	return 0;
}

int Dinic()
{
	int ans=0;
	while(bfs()>0)
	{
		for(int i=0;i<=tot;i++)//每一次建立完分层图后都要把cur置为每一个点的第一条边
		  cur[i]=head[i];
		while(int flow=dfs(s,inf))
		   ans+=flow;
	}
	return ans;
}

int main()
{
	scanf("%d%d%d",&n,&m,&k);
	init();

	for(int i=1;i<=n;i++)//将源点与每个奥特曼连起来
	add(s,i,1);         //其中路径最大流量设置为1
	
	add(s,t+1,k);    //将源点与k瓶药的p点连起来,路径最大流量为k
	
	for(int i=1;i<=n;i++)//将p点与奥特曼连起来
	add(t+1,i,1);
	
	for(int i=1;i<=n;i++)
	{
		int u,l;
		scanf("%d",&l);
		for(int j=1;j<=l;j++)
		{
			scanf("%d",&u);
			add(i,u+n,inf);//将奥特曼和怪兽连起来
		}
	}
	
	for(int i=1;i<=m;i++)//将怪兽和汇点连起来
	add(n+i,t,1);
	
	int ans=Dinic();
	printf("%d\n",ans);//最后输出最大流就可以了
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值