P1231 教辅的组成

教辅的组成

Luogu P1231

题目背景

滚粗了的 HansBug 在收拾旧语文书,然而他发现了什么奇妙的东西。

题目描述

蒟蒻 HansBug 在一本语文书里面发现了一本答案,然而他却明明记得这书应该还包含一份练习题。然而出现在他眼前的书多得数不胜数,其中有书,有答案,有练习册。已知一个完整的书册均应该包含且仅包含一本书、一本练习册和一份答案,然而现在全都乱做了一团。许多书上面的字迹都已经模糊了,然而 HansBug 还是可以大致判断这是一本书还是练习册或答案,并且能够大致知道一本书和答案以及一本书和练习册的对应关系(即仅仅知道某书和某答案、某书和某练习册有可能相对应,除此以外的均不可能对应)。既然如此,HansBug 想知道在这样的情况下,最多可能同时组合成多少个完整的书册。

输入格式

第一行包含三个正整数 \(N_1\)\(N_2\)\(N_3\),分别表示书的个数、练习册的个数和答案的个数。

第二行包含一个正整数 \(M_1\),表示书和练习册可能的对应关系个数。

接下来 \(M_1\) 行每行包含两个正整数 \(x\)\(y\),表示第 \(x\) 本书和第 \(y\) 本练习册可能对应。(\(1\)\(\leq\)\(x\)\(\leq\)\(N_1\)\(1\)\(\leq\)\(y\)\(\leq\)\(N_2\)

\(M_{1+3}\) 行包含一个正整数 \(M_2\),表述书和答案可能的对应关系个数。

接下来 \(M_2\) 行每行包含两个正整数 \(x\)\(y\),表示第 \(x\) 本书和第 \(y\) 本答案可能对应。(\(1\)\(\leq\)\(x\)\(\leq\)\(N_1\)\(1\)\(\leq\)\(y\)\(\leq\)\(N_3\)

输出格式

输出包含一个正整数,表示最多可能组成完整书册的数目。

样例 #1

样例输入 #1

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

样例输出 #1

2

提示

样例说明:

如题,\(N_1=5\)\(N_2=3\)\(N_3=4\),表示书有 \(5\) 本、练习册有 \(3\) 本、答案有 \(4\) 本。

\(M_1=5\),表示书和练习册共有 \(5\) 个可能的对应关系,分别为:书 4 和练习册 3、书 2 和练习册 2、书 5 和练习册 2、书 5 和练习册 1 以及书 5 和练习册 3。

\(M_2=5\),表示数和答案共有 \(5\) 个可能的对应关系,分别为:书 1 和答案 3、书 3 和答案 1、书 2 和答案 2、书 3 和答案 3 以及书 4 和答案 3。

所以,以上情况的话最多可以同时配成两个书册,分别为:书 2 + 练习册 2 + 答案 2、书 4 + 练习册 3 + 答案 3。

数据规模:

对于数据点 \(1\), \(2\), \(3\)\(M_1\), \(M_2\leq 20\)

对于数据点 \(4-10\)\(M_1\), \(M_2 \leq 20000\)

Solution

看到这类的求最大匹配的问题,很容易可以想到建图然后用网络流来解决,可以很轻松地建出这个图(样例):

321book2book4book5book1book3_3_1_2ST

但这样不难发现会出现一本书搭配了多本练习册和答案这种情况,与题目条件不符。解决方法就是将中间的书拆点并在中间连一条容量为 \(1\) 的边即可。

321book2book4book5_3_1_2STbook_1book_3book_2book_4book1book3book_5

最后对这张图跑一个dinic算法即可。

Code

需要注意建图的问题,并且边的数量不止 \(4e4\),需要开的更大,否则会导致 RE

#include<bits/stdc++.h>
using namespace std;
template<typename T> void read(T &k)
{
	k=0;T flag=1;char b=getchar();
	while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
	while (isdigit(b)) {k=k*10+b-48;b=getchar();}
	k*=flag;
}
template<typename T> void write(T k) {if (k<0) putchar('-'),write(-k);if (k>9) write(k/10);putchar(k%10+48);}
template<typename T> void writewith(T k,char c) {write(k);putchar(c);}
const int _SIZE=1e4;
int S,T;
int n1,n2,n3,m1,m2;
struct EDGE{
	int nxt,to,len;
}edge[(_SIZE<<5)+5];
int tot=1,head[(_SIZE<<2)+5],cur[(_SIZE<<2)+5],dist[(_SIZE<<2)+5];
int id(int x,int v)
{
	int ind[6]={0,0,n2,n2+n1,n2+n1+n1};
	return ind[x]+v;
}
void AddEdge(int x,int y,int len)
{
	edge[++tot]=(EDGE){head[x],y,len};
	head[x]=tot;
	edge[++tot]=(EDGE){head[y],x,0};
	head[y]=tot;
}
bool bfs()
{
	queue<int> q;
	q.push(S);
	memset(dist,-1,sizeof(dist));
	dist[S]=0;
	while (!q.empty())
	{
		int u=q.front();q.pop();
		for (int i=head[u];i;i=edge[i].nxt)
		{
			int twd=edge[i].to;
			if (dist[twd]!=-1 || edge[i].len<=0) continue;
			q.push(twd);
			dist[twd]=dist[u]+1;
			if (twd==T) return 1;
		}
	}
	return 0;
}
int dfs(int x,int f)
{
	if (f==0 || x==T) return f;
	int used=0;
	for (int &i=cur[x];i;i=edge[i].nxt)
	{
		int twd=edge[i].to;
		if (edge[i].len && dist[twd]==dist[x]+1)
		{
			int w=dfs(twd,min(f,edge[i].len));
			if (!w) continue;
			used+=w,f-=w;
			edge[i].len-=w,edge[i^1].len+=w;
			if (f==0) break;
		}
	}
	if (!used) dist[x]=-1;
	return used;
}
int dinic()
{
	int mflow=0;
	while (bfs())
	{
		memcpy(cur,head,sizeof(head));
		mflow+=dfs(S,INT_MAX);
	}
	return mflow;
}
int main()
{
	read(n1),read(n2),read(n3);
	S=n2+n1*2+n3+1;T=S+1;
	read(m1);
	for (int i=1;i<=m1;i++)
	{
		int u,v;read(u),read(v);
		AddEdge(id(1,v),id(2,u),1);
	}
	read(m2);
	for (int i=1;i<=m2;i++)
	{
		int u,v;read(u),read(v);
		AddEdge(id(3,u),id(4,v),1);
	}
	for (int i=1;i<=n1;i++) AddEdge(id(2,i),id(3,i),1);
	for (int i=1;i<=n2;i++) AddEdge(S,id(1,i),1);
	for (int i=1;i<=n3;i++) AddEdge(id(4,i),T,1);
	int ans=dinic();
	writewith(ans,'\n');
	return 0;
}
posted @ 2022-08-17 14:59  Hanx16Msgr  阅读(22)  评论(0编辑  收藏  举报