(有任何问题欢迎留言或私聊
题目:hdu4292传送门
题目大意:
有n个人,num1种食物,num2种饮料。每种食物和每种饮料是有限的。每个人有自己喜欢的食物和饮料的种类。问最多有多少人选到自己喜欢的食物和饮料的搭配方案。
每个人只能选一个食物和一杯饮料!
思路:
这是一道最大流专题里的题目。拿到题很容易想到这一种建图方式。
超级源点S向每种食物连边,流量为该种食物的数量。
每种饮料向超级汇点T连边,流量为该种饮料的数量。
每个人向自己喜欢的饮料和食物连边,流量为1。
一开始肯定会有这种建图的想法。但是这样还不够。
题目说了,每个人只能选一杯饮料和一个食物。这样建图很明显不能保证每个人只选了一杯饮料和一个食物。还有问题。
举个栗子:
只有一个人,他喜欢3种食物,3种饮料。每种食物和饮料的数量为1.
把这个图跑一边最大流你会发现你得到的答案是3,而不是1!
为什么呢?因为你没处理每个人只能选一个食物和饮料的限制条件。
解决方法:
把每个人拆成两个点,不妨称作左点 i 和右点i + n。左点和右点连边,流量设为1.
重新建图:
每种食物向喜欢它的人的左点 i 连边,流量为1.
每个人的右点 i + n 向他喜欢的饮料连边,流量为1.
然后跑最大流就okey了。
这样新图就建好了。这就是网络流中的一种常用技巧:拆点。
处理有限制条件的图的时候经常使用 !
AC代码:
//c++ 202ms
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
const int MX=805;
char ar[MX];
int s,t,e,fr[MX],h[MX],nh[MX];
LL ans;
int n,num1,num2;
const LL inf=1e17;
inline int rd(){
int x=0;
char c=getchar();
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x;
}
struct lp{
int t,ne;
LL w;
}a[MX*1000];
void ins(int f,int t,LL w){
a[++e].t=t,a[e].ne=fr[f],fr[f]=e,a[e].w=w;
a[++e].t=f,a[e].ne=fr[t],fr[t]=e,a[e].w=0;
}
LL Min(LL x,LL y){
return x<y?x:y;
}
LL sap(int u,LL fl){
if (u==t) return fl;
LL res=fl;
for (int i=fr[u];i;i=a[i].ne){
if (a[i].w&&h[a[i].t]+1==h[u]){
LL t=sap(a[i].t,Min(res,a[i].w));
a[i].w-=t,a[i^1].w+=t;
if (!(res-=t)) return fl;
}
}
if (!(--nh[h[u]])) h[t]=t;
++nh[++h[u]];
return fl-res;
}
int main(){
while(~scanf("%d%d%d",&n,&num1,&num2)){
memset(fr,0,sizeof(fr));
memset(nh,0,sizeof(nh));
memset(h,0,sizeof(h));
s=1,t=2*n+num1+num2+2;ans=0;e=1;
int x;
for(int i=0;i<n;++i)ins(i+2,i+2+n,1);
for(int i=0;i<num1;++i){
scanf("%d",&x);
ins(s,2*n+i+2,x);
}
for(int i=0;i<num2;++i){
scanf("%d",&x);
ins(2*n+i+2+num1,t,x);
}
for(int i=0;i<n;++i){
scanf("%s",ar);
for(int j=0;j<num1;++j){
if(ar[j]=='Y')ins(2*n+j+2,i+2,1);
}
}
for(int i=0;i<n;++i){
scanf("%s",ar);
for(int j=0;j<num2;++j){
if(ar[j]=='Y')ins(i+2+n,2*n+j+2+num1,1);
}
}
nh[0]=t;
while (h[t]!=t){
ans+=sap(s,inf);
}
printf("%lld\n",ans);
}
return 0;
}
OvO