这是前天做的一道题。。今天补发一下。。
还是网络流,关键在于图怎么建
把每个人拆成两个点,如果男孩 i 喜欢女孩 j ,则连接点a1i和 b1j,流量为1;
如果不喜欢,则连接a2i 和 b2j,流量为1;
然后一步是关键,因为每个人都不能和不喜欢的人跳多于K支舞,
所以把a1i 和 a2i 连起来,流量为K;b2i 和b1i 连起来,流量为K(注意方向)
然后把源点和a1 各点连起来,b1各点和汇点连起来,流量为mx;
符合条件的mx 的最大值就是答案了(每个人都跳了mx支舞)
怎么判断mx是否符合条件呢?
当每个人都跳了mx支舞,即网络的流量ans==mx * n就行了
所以二分答案,再判断可行性就行了
坑爹的BZOJ输入数据行末有空格害得我WA了好几次查错又查不出T_T
代码:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#define maxn 1100
#define maxe 500100
#define g getchar()
#define inf 0x3f3f3f3f
using namespace std;
struct re{int v,fl,next;}ed[maxe];
int e,n,k,answer;
int head[maxn],pd[maxn],dui[maxe];
char ch;
bool map[100][100];
inline void ins(int x,int y,int w){
ed[++e].v=y;ed[e].fl=w;ed[e].next=head[x];head[x]=e;
ed[++e].v=x;ed[e].fl=0;ed[e].next=head[y];head[y]=e;
}
inline int min(int x,int y){
return x<y?x:y;
}
void init(){
scanf("%d%d",&n,&k);
memset(map,0,sizeof(map));
for(int i=1;i<=n;++i){
char ch[51];
scanf("%s",ch);
for(int j=1;j<=n;j++)
if(ch[j-1]=='Y')map[i][j]=1;
}
}
void makemap(int w){ //建图
e=0;
memset(head,0,sizeof(head));
for(int i=1;i<=n;++i){
ins(0,i,w);
ins(i,i+n,k);
ins(3*n+i,2*n+i,k);
ins(2*n+i,4*n+1,w);
for(int j=1;j<=n;++j)
if(map[i][j])ins(i,n*2+j,1);else ins(i+n,n*3+j,1);
}
}
bool bfs(){
int tou=1,wei=1;
memset(pd,-1,sizeof(pd));
dui[1]=0;pd[0]=0;
for(;tou<=wei;++tou){
int u=dui[tou];
for(int i=head[u];i;i=ed[i].next){
if(ed[i].fl&&pd[ed[i].v]<0){
pd[ed[i].v]=pd[u]+1;
dui[++wei]=ed[i].v;
}
}
}
return pd[4*n+1]!=-1;
}
int dfs(int x,int w){
if(x==4*n+1)return w;
int le,used=0;
for(int i=head[x];i;i=ed[i].next){
if(pd[ed[i].v]==pd[x]+1&&ed[i].fl){
le=w-used;
le=dfs(ed[i].v,min(le,ed[i].fl));
used+=le;
ed[i].fl-=le;
ed[i%2?i+1:i-1].fl+=le;
if(used==w)return w;
}
}
if(!used)pd[x]=-1;
return used;
}
void work(){ //二分答案
int l=0,r=50,mid;
while(l<=r){
mid=(l+r)>>1;
makemap(mid);
int ans=0;
while(bfs())ans+=dfs(0,inf);
if(ans==n*mid){
answer=mid;l=mid+1;
}
else r=mid-1;
}
}
int main(){
init();
work();
printf("%d\n",answer);
return 0;
}
类似的网络流 戳这儿