poj1698 - Alice's Chance (最大流)

题目大意:爱丽丝要拍电影,有n部电影,规定爱丽丝每部电影在每个礼拜只有固定的几天可以拍电影,只可以拍前面w个礼拜,并且这部电影要拍d天,问爱丽丝能不能拍完所有的电影
第一行代表有多少组数据
对于每组数据第一行代表有n部电影
接下来2到n+1行,每行代表一个电影,每行9个数,前面7个数,1代表拍,0代表不拍,第8个数代表要拍几天,第9个数代表有几个礼拜时间拍


解题思路:
这题可以看做成二分图多重匹配,也可以用网络流实现,主要是建图,将图建好了就好说了
用s=0表示源点,t=371表示汇点....
1-20表示电影,因为电影最多只有20部

将371看做汇点的原因是,21-370表示每天,因为最多有50个星期
源点指向每部电影,最大容量为这部电影所拍摄的天数
电影指向天数,因为每天只能拍一部电影,若这天可以拍这部电影就表示最大容量为1
天数都指向汇点,最大容量都为1
这样建好图之后就可以直接从源点到汇点求最大流,看最大流是否等于每个电影天数相加只和,相等则可以拍完,不等则拍不完


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <memory.h>
#include <queue>
using namespace std;

#define inf 0x7ffffff
#define N 500
#define M 20000
int dis[N], cur[N],gap[N],head[N],pre[N];
int nv,ne,s,t,top,max_week,sum;

struct node
{
	int u,v,c,next;
}edge[M];
void init()
{
    top = 0;
    memset(head,-1,sizeof(head));
    s = 0; // start position
    max_week = -1;
    sum = 0;
}
void add_edge(int u,int v, int c)
{
   edge[top].u=u;
   edge[top].v=v;
   edge[top].c=c;
   edge[top].next=head[u];
   head[u]=top++;
   edge[top].u=v;
   edge[top].v=u;
   edge[top].c=0;
   edge[top].next=head[v];
   head[v]=top++;	
}

int sap()
{
    
    int flow=0,max_flow=inf,u,v;
    memset(dis,0,sizeof(dis));
	memset(gap,0,sizeof(gap));
    for(int i=0; i<nv; i++)          cur[i]=head[i];         
    gap[s]=nv;
    u=pre[s]=s;
    while(dis[s]<nv)
    {
        
        loop :
        for(int &j=cur[u]; j!=-1; j=edge[j].next)
        {
             v=edge[j].v;
            if(edge[j].c>0&&dis[u]==dis[v]+1)
            {
                
                if(edge[j].c<max_flow) max_flow=edge[j].c;
                pre[v]=u;
                u=v;
                if(v==t)     
				{
					for(u=pre[v];v!=s;v=u,u=pre[u])
					{
						edge[cur[u]].c-=max_flow;
						edge[cur[u]^1].c+=max_flow;
					}
					flow+=max_flow;
				    max_flow=inf;
				}              
               goto loop ;
            }
        }
        int mindis=nv;
        for(int j=head[u]; j!=-1; j=edge[j].next)
        {
             v=edge[j].v;
            if(edge[j].c>0&&dis[v]<mindis)
            {
                mindis=dis[v];
                cur[u]=j;
            }
        }
        if((--gap[dis[u]])==0)
            break;
        gap[dis[u]=mindis+1]++;
        u=pre[u];
    }
    return flow;
}

int main()
{
    int i,j,k,h,time,ans,n,work[7],day,week;
    scanf("%d",&time);
    while(time--)
    {
        init();
        scanf("%d",&n);
        for(i = 1; i <= n; ++i)
        {
            for(j = 0;j < 7; ++j)
                scanf("%d",&work[j]);
            scanf("%d%d",&day,&week);
            add_edge(s,i,day);     //源点到电影连边 
            for(j = 0;j < week; ++j)
                for(k = 0;k < 7; ++k)
                    if(work[k]) // can do 
                        add_edge(i,n+j*7+k+1,inf);  //电影到固定的礼拜连边 
            max_week = max_week>week?max_week:week;    //最多到几周 
            sum += day;
        }
        t = n+max_week*7+1;
        nv = t + 1;
        for(i = 0;i < max_week; ++i )
            for(j = 1;j <= 7; ++j)
                add_edge(n+i*7+j,t,1);  //天数与汇点连边 
        ans = sap();
       // printf("%d ",ans);
        if(sum == ans)
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值