您的位置:首页 > 其它

【最大流】BZOJ1305-[CQOI2009]dance跳舞

2016-08-09 22:04 441 查看
【题目大意】

一次舞会有n个男孩和n个女孩。每首曲子开始时,所有男孩和女孩恰好配成n对跳交谊舞。每个男孩都不会和同一个女孩跳两首(或更多)舞曲。有一些男孩女孩相互喜欢,而其他相互不喜欢(不会“单向喜欢”)。每个男孩最多只愿意和k个不喜欢的女孩跳舞,而每个女孩也最多只愿意和k个不喜欢的男孩跳舞。给出每对男孩女孩是否相互喜欢的信息,舞会最多能有几首舞曲?

【思路】

假设当前有a首舞曲。

把每个人拆成两个点,从“喜欢”到“不喜欢”连一条容量为k的边。从S往“男孩喜欢”连一条容量为a的边,从“女孩喜欢”往T连一条容量为a的边。

然后对于每对男孩女孩,如果不喜欢,则从“男孩不喜欢”到“女孩不喜欢”连一条容量为1的边,否则从“男孩喜欢”到“女孩喜欢”连一条容量为1的边。

为什么这个是正确的呢?这样相当于喜欢的人之间限制住了至多跳一首,而最多和k个不喜欢的人跳舞。画图感受一下就好了。

如果这a首舞曲都能用到,那么这个网络流应该是满流的。

所以二分答案。

注意很重要的两点:

①二分最后ub还要单独判断一下。

②不要忘了每次E的容量会被修改,所以暂存到rE每次重新回到原始状态。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define INF 0x7fffffff
#define S 0
#define T 4*n+1
using namespace std;
struct node
{
int to,pos,cap;
};
const int MAXN=50+5;
int n,k;
vector<node> E[MAXN*4+2];
vector<node> tmpE[MAXN*4+2];
int dis[MAXN*4+2];

void addedge(int u,int v,int w)
{
tmpE[u].push_back((node){v,tmpE[v].size(),w});
tmpE[v].push_back((node){u,tmpE[u].size()-1,0});
E[u].push_back((node){v,E[v].size(),w});
E[v].push_back((node){u,E[u].size()-1,0});
}

bool bfs()
{
memset(dis,-1,sizeof(dis));
queue<int> que;
while (!que.empty()) que.pop();
que.push(S);
dis[S]=0;
while (!que.empty())
{
int head=que.front();que.pop();
if (head==T) return true;    //首次抵达T即可返回,不需要整张图全部分层
for (int i=0;i<E[head].size();i++)
{
node tmp=E[head][i];
if (dis[tmp.to]==-1 && tmp.cap)
{
dis[tmp.to]=dis[head]+1;
que.push(tmp.to);
}
}
}
return false;
}

int dfs(int s,int e,int f)
{
if (s==e) return f;
int ret=0;
for (int i=0;i<E[s].size();i++)
{
node &tmp=E[s][i];
if (dis[tmp.to]==dis[s]+1 && tmp.cap)
{
int delta=dfs(tmp.to,e,min(f,tmp.cap));
if (delta>0)
{
tmp.cap-=delta;
E[tmp.to][tmp.pos].cap+=delta;
f-=delta;
ret+=delta;
if (f==0) return ret;
}
else dis[tmp.to]=-1;
}
}
return ret;
}

int dinic()
{
int flow=0;
while (bfs())
{
int f=dfs(S,T,INF);
if (f) flow+=f;else break;
}
return flow;
}

void init()
{
scanf("%d%d",&n,&k);
//0 源点
//1~n 男性喜欢
//n+1~2n 男性不喜欢
//2n+1~3n 女性不喜欢
//3n+1~4n 女性喜欢
//4n+1 汇点
for (int i=1;i<=n;i++) addedge(i,i+n,k);
for (int i=2*n+1;i<=3*n;i++) addedge(i,i+n,k);
for (int i=1;i<=n;i++)
{
char str[MAXN];
scanf("%s",str+1);
for (int j=1;j<=n;j++)
{
if (str[j]=='Y') addedge(i,3*n+j,1);
else addedge(n+i,2*n+j,1);
}
}
for (int i=1;i<=n;i++) addedge(S,i,0);
for (int i=3*n+1;i<=4*n;i++) addedge(i,T,0);
}

void solve()
{
int lb=0,ub=n;
while (lb+1<ub)
{
for (int i=S;i<=T;i++)
for (int j=0;j<E[i].size();j++) E[i][j]=tmpE[i][j];
int mid=(lb+ub)>>1;
for (int i=0;i<n;i++) E[S][i].cap=mid;
for (int i=3*n+1;i<=4*n;i++) E[i][E[i].size()-1].cap=mid;
int d=dinic();
if (d==mid*n) lb=mid;else ub=mid;
}
for (int i=S;i<=T;i++)
for (int j=0;j<E[i].size();j++) E[i][j]=tmpE[i][j];
for (int i=0;i<n;i++) E[S][i].cap=ub;
for (int i=3*n+1;i<=4*n;i++) E[i][E[i].size()-1].cap=ub;
int d=dinic();
printf("%d",(d==ub*n)?ub:lb);
}

int main()
{
init();
solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: