您的位置:首页 > 其它

A - Alice's Chance POJ - 1698 二分图多重匹配||最大流

2017-04-26 16:02 387 查看
传送门:POJ1698

题意:爱丽丝要拍电影,有n部电影,规定爱丽丝每部电影在每个礼拜只有固定的几天可以拍,电影i必须在w[i]个礼拜内拍完,并且电影i爱丽丝至少要拍d[i]天,问爱丽丝能不能拍完所有的电影.

思路:一开始只想到了是网络流,我的建图方法是增加一个源点和汇点,从source到每一天建立权值为1的边,如果某一天能拍某部电影,则建立对应的权值为1的边,然后再将每部电影和sink建边,权值为inf,跑最大流判断是否大于总天数,后来想明白这样根本没用到d[i]限制,就有可能存在某部电影用了很多天(即使他原本不用这么多天),有的电影却天数不够,这样最大流还是有可能大于总天数,然而其实是不能拍完的。

这题正解是二分图多重匹配。

求解二分图多重匹配基本步骤:

图分为x部和y部

1.建立一个源点S和汇点T.

2.S指向x部顶点,容量为x内顶点的L值.y顶点指向T,容量为y内顶点的L值.(L[i]为点i的匹配上限)

3.原图中的各边在新图中仍存在,容量为1.

那么S到T的最大流就是多重最大匹配.


本题建图方法:

用0表示source,1-350表示每一天,351-370表示每一场电影,大于370的任意一点表示sink,电影是x部点,天数是y部点,按照上面的方法建图就好了,最后求出最大流如果大于等于总天数,则说明可以拍完。

代码:

//ISAP int
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define ll long long
#define MAXN 1000
#define inf 0x3f3f3f3f
using namespace std;
int n,m;//题目输入点数,边数
struct Edge{
int v,next;
int cap,flow;
}edge[MAXN*1000];
int cur[MAXN],pre[MAXN],gap[MAXN],path[MAXN],dep[MAXN];
int cnt=0;//实际存储总边数
void init()
{
cnt=0;
memset(pre,-1,sizeof(pre));
}
void add(int u,int v,int w,int rw=0)//加边 有向图三个参数,无向图四个
{
edge[cnt].v=v;
edge[cnt].cap=w;
edge[cnt].flow=0;
edge[cnt].next=pre[u];
pre[u]=cnt++;
edge[cnt].v=u;
edge[cnt].cap=rw;
edge[cnt].flow=0;
edge[cnt].next=pre[v];
pre[v]=cnt++;
}
bool bfs(int s,int t)//其实这个bfs可以融合到下面的迭代里,但是好像是时间要长
{
memset(dep,-1,sizeof(dep));
memset(gap,0,sizeof(gap));
gap[0]=1;
dep[t]=0;
queue<int>q;
while(!q.empty())
q.pop();
q.push(t);//从汇点开始反向建层次图
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=pre[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(dep[v]==-1&&edge[i^1].cap>edge[i^1].flow)//注意是从汇点反向bfs,但应该判断正向弧的余量
{
dep[v]=dep[u]+1;
gap[dep[v]]++;
q.push(v);
//if(v==s)//感觉这两句优化加了一般没错,但是有的题可能会错,所以还是注释出来,到时候视情况而定
//break;
}
}
}
return dep[s]!=-1;
}
int isap(int s,int t)
{
bfs(s,t);
memcpy(cur,pre,sizeof(pre));
int u=s;
path[u]=-1;
int ans=0;
while(dep[s]<n)//迭代寻找增广路
{
if(u==t)
{
int f=inf;
for(int i=path[u];i!=-1;i=path[edge[i^1].v])//修改找到的增广路
f=min(f,edge[i].cap-edge[i].flow);
for(int i=path[u];i!=-1;i=path[edge[i^1].v])
{
edge[i].flow+=f;
edge[i^1].flow-=f;
}
ans+=f;
u=s;
continue;
}
bool flag=false;
int v;
for(int i=cur[u];i!=-1;i=edge[i].next)
{
v=edge[i].v;
if(dep[v]+1==dep[u]&&edge[i].cap-edge[i].flow)
{
cur[u]=path[v]=i;//当前弧优化
flag=true;
break;
}
}
if(flag)
{
u=v;
continue;
}
int x=n;
if(!(--gap[dep[u]]))return ans;//gap优化
for(int i=pre[u];i!=-1;i=edge[i].next)
{
if(edge[i].cap-edge[i].flow&&dep[edge[i].v]<x)
{
x=dep[edge[i].v];
cur[u]=i;//常数优化
}
}
dep[u]=x+1;
gap[dep[u]]++;
if(u!=s)//当前点没有增广路则后退一个点
u=edge[path[u]^1].v;
}
return ans;
}
int week[10];
int main()
{
int T,t,d,w,source,sink;
cin>>T;
while(T--)
{
bool flag=0;
init();
source=0;
sink=n=380;//大于370就行
int a,b,c,day=0;
cin>>t;
for(int i=1;i<=t;i++)
{
for(int j=1;j<=7;j++)
{
cin>>week[j];
}
cin>>d>>w;
add(source,350+i,d);
day+=d;
for(int j=1;j<=7;j++)
{
if(week[j])
for(int k=0;k<w;k++)
{
add(350+i,7*k+j,1);
}
}
}
for(int i=1;i<=350;i++)
add(i,sink,1);
int ans=isap(source,sink);
//cout<<ans<<endl;
if(ans>=day)
puts("Yes");
else
puts("No");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: