时间限制: 1 Sec 内存限制: 128 MB
提交: 165 解决: 26
[提交] [状态] [命题人:admin]
题目描述
冬之国有N个勇士,他们要屠M条龙,每个勇士只能够屠一条龙。
每个勇士的技能属性不同,因此他们能够屠的龙也不同,第i个勇士可以屠编号在集合Si内的龙。
作为冬之国最恶毒的女巫小M,她有K瓶药水,每一瓶药水可以使得一个勇士在屠完一条龙之后不会疲倦,当然药水药效有限,只能够支持勇士们再多屠一条龙。同时小M配的药水有一定的副作用,因此每个勇士最多只能喝一瓶药水。
现在小M想要知道,在她的帮助下,勇士们最多能屠多少条龙。
输入
第一行三个正整数N,M,K。
接下来N行,每行|Si|+1个正整数,首先是|Si|描述集合大小,然后是这个集合中的元素。
输出
输出一行一个整数表示答案。
复制样例数据
3 5 2
4 1 2 3 5
2 2 5
2 1 2
样例输出
4
提示
对于100%的数据,N,M,K≤500。
[提交][状态]
GPLv2 licensed by HUSTOJ 2019
最大流板子、
也可以写匈牙利
我本来写匈牙利的,调了半天没调出来,拿最大流dinic过了后,对拍,把匈牙利的hack点找出来的两个,然后就A了
目前我的匈牙利可以A掉upc的药水(别的地方没试过)
最大流的话,建图如下,就是建完图套个板子
代码
#include
using namespace std;
typedef long long ll;
const int maxn = 1e3+7,mod = 1e9+7;
const ll inf = 1e16;
struct node{
ll t,cap,flow,next;
}e[maxn*maxn];
int head[maxn],cur[maxn],cnt;
void add(int u,int v,ll cap){
e[cnt]=node{v,cap,0,head[u]};
head[u]=cnt++;
e[cnt]=node{u,0,0,head[v]};
head[v]=cnt++;
}
int d[maxn];
bool bfs(int s,int t){
memset(d,0,sizeof(d));
queueq;
q.push(s);
d[s]=1;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];~i;i=e[i].next){
int v=e[i].t;
if(d[v]==0&&e[i].cap-e[i].flow>0){
d[v]=d[u]+1;
q.push(v);
}
}
}
return d[t]>0;
}
ll dfs(int s,int t,ll minedge){
if(s==t)return minedge;
ll flow=0;
for(int &i=cur[s];~i;i=e[i].next){
int v=e[i].t;
if(d[v]==d[s]+1&&e[i].cap-e[i].flow>0){
ll temp=dfs(v,t,min(minedge-flow,e[i].cap-e[i].flow));
e[i].flow+=temp;
e[i^1].flow-=temp;
flow+=temp;
if(flow==minedge)return flow;
}
}
if(flow==0)d[s]=0;
return flow;
}
ll dinic(int s,int t){
ll maxflow=0;
while(bfs(s,t)){
memcpy(cur,head,sizeof(head));
maxflow+=dfs(s,t,inf);
}
return maxflow;
}
int main(){
memset(head,-1,sizeof head);
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
for(int i=1,k;i<=n;i++){
scanf("%d",&k);
for(int j=1,x;j<=k;j++){
scanf("%d",&x);
add(i,n+x,1);
}
}
for(int i=1;i<=n;i++)add(n+m+1,i,1);//s
for(int i=1;i<=m;i++)add(n+i,n+m+2,1);//t
for(int i=1;i<=n;i++)add(n+m+3,i,1);//k
add(n+m+1,n+m+3,k);
ll ans = dinic(n+m+1,n+m+2);
printf("%lld\n",ans);
return 0;
}
匈牙利的话
大体思路:
考虑多开一倍的点(因为最多喝一瓶药),直接跑匈牙利,然后限制答案在(n+k)范围内
仔细看,如果k大于n的话,n+k那就不对了(最多喝一瓶,这样明显多于2*n了),所以
限制范围在n+min(n,k),然后我就没再想出来hack点
今天对拍后,得到下面这个图
突然惊醒,不能是n+min(n,k)
因为人数可能比n要小,这里统计的人数为cnt个
答案就应该限制在cnt+min(cnt,k)
然后我一交,wa了
继续对拍,得到下面这个图
这个算出来的话是4,但是答案实际上是3
我想了想,不应该是在cnt个人的基础上增加min(cnt,k)
而是应该在原先图的基础上跑一个匈牙利得到tans,然后再考虑增加min(cnt,k)
然后我又一交,嗯嗯,A了
代码
#include
using namespace std;
typedef long long ll;
const int maxn = 1e3+7;
int mmp[maxn][maxn];
int last_match[maxn];
bool vis[maxn];
int n,m,k;
bool dfs(int u){
for(int i=1;i<=m;i++)if(mmp[u][i]){
if(!vis[i]){
vis[i] = 1;
if(!last_match[i]||dfs(last_match[i]))
return last_match[i] = u,1;
}
}
return 0;
}
int Hry(int ans = 0){
memset(last_match,0,sizeof last_match);
for(int i=1;i<=n;i++){
memset(vis,0,sizeof vis);
if(dfs(i))
ans++;
}
return ans;
}
int main(){
scanf("%d%d%d",&n,&m,&k);
int cnt = 0;
for(int i=1,k;i<=n;i++){
scanf("%d",&k);
if(k)cnt++;
for(int j=1,x;j<=k;j++){
scanf("%d",&x);
mmp[i][x] = mmp[i+n][x] = 1;
}
}
int tans = Hry();
n <<= 1;
int ans = Hry();
n >>= 1;
ans = min(ans,tans+min(cnt,k));
printf("%d\n",ans);
return 0;
}
/*
5 5 0
0
4 1 2 3 4
2 2 4
1 3
1 3
*/