POJ-1698 Alice's Chance(最大流)

题意:

Alice去拍电影,每一部电影只可以在某一天进行拍摄(星期一到星期日,1表示可以,0表示不可以),以及这部电影需要拍摄的总天数和在第几个星期前需要拍摄完成,且Alice每天只能拍一部电影。问Alice能够拍上全部的电影吗?可以输出Yes,否则输出No。

构图:

建立一个源点S和一个汇点T,然后因为Alice能选择某个日子进行拍电影,所以所有日子都可以与S连边(S->),再根据哪些日子可用来拍哪个电影去进行日子与电影节点的建边,因为一个日子只能给某个电影提供1天的工作,所以权值为1,然后电影与汇点进行连边,因为要限制电影消耗的日子,即如果某个电影已经有足够的时间了,不必浪费时间再流给它,所以电影与汇点进行连边权值为电影需要拍的日子,从而限制了该电影有足够日子后对日子的浪费。

将题目条件一一罗列出来,然后根据题目语义将问题抽象成网络图进行问题解决。(链接)


本题EK也可以过,只给出SAP方法代码:

#include <algorithm>
#include <string.h>
#include <cstdio>
#include <queue>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 377;
const int maxm = maxn*100;	//一定要仔细计算一下边的数量 
const int BAS = 50*7;
struct node
{
	int v, w, next;
} edge[maxm];
int no, head[maxn], now[maxn];
int n;
int day[10], sum;
int N, S, T;
int dis[maxn], pre[maxn], rec[maxn], gap[maxn];
queue<int> q;
inline void init()
{
	no = 0;
	memset(head, -1, sizeof head);
}
inline void add(int u, int v, int w)
{
	edge[no].v = v, edge[no].w = w;
	edge[no].next = head[u]; head[u] = no++;
	
	edge[no].v = u, edge[no].w = 0;
	edge[no].next = head[v]; head[v] = no++;
}
int mapping()
{
	int d, w, flag, judge;
	scanf("%d", &n); 
	flag = 1; sum = 0;
	S = BAS+21, T = BAS+22; N = 0;
	for(int j = 1; j <= n; ++j)
	{
		judge = 0;
		for(int i = 1; i <= 7; ++i) 
		{
			scanf("%d", &day[i]);
			if(day[i]) ++judge;
		}
		scanf("%d %d", &d, &w); 
		sum += d;
		for(int i = 1; i <= 7; ++i)
		{
			if(day[i]) 
			for(int k = 0; k < w; ++k) add(i+k*7, BAS+j, 1);
		}
		add(BAS+j, T, d);
		N = max(w, N);
		if(judge*w < d) flag = 0;//特判,当一个电影可用的所有时间小于要求时间直接No 
	}
	for(int i = 1; i <= 7; ++i)
	for(int k = 0; k < N; ++k) add(S, i+k*7, 1);
	N *= 7;
	if(flag) return 1;
	return 0;
}
void prepare(int S, int T)
{
	memset(gap, 0, sizeof gap);
	for(int i = 1; i <= N; ++i) now[i] = head[i];
	for(int i = 1; i <= 22; ++i) now[BAS+i] = head[BAS+i];
	while(!q.empty()) q.pop();
	memset(dis, 0x3f, sizeof dis);
	dis[T] = 0; q.push(T);
	while(!q.empty())
	{
		int top = q.front(); q.pop();
		++gap[dis[top]];
		for(int k = head[top]; k != -1; k = edge[k].next)
		{
			if(edge[k^1].w && dis[edge[k].v] == inf)
			dis[edge[k].v] = dis[top]+1, q.push(edge[k].v);
		}
	}
}
int SAP(int S, int T)
{
	int k, top = S, ans = 0, flow = inf;
	prepare(S, T);
	pre[S] = S;
	while(dis[S] < maxn)//切记此处与节点数比较,因为通过方向变会造成距离可能达到节点数 
	{
		if(top == T)
		{
			ans += flow;
			for(; top != S; top = pre[top])
			{
				edge[rec[top]].w -= flow;
				edge[rec[top]^1].w += flow;
			}
			flow = inf;
		}
		for(k = now[top]; k != -1; k = edge[k].next)
		{
			if(edge[k].w && dis[top] == dis[edge[k].v]+1)
			{
				rec[edge[k].v] = k; pre[edge[k].v] = top;
				flow = min(flow, edge[k].w);
				now[top] = k; top = edge[k].v;
				break;
			}
		}
		if(k == -1)
		{
			if(--gap[dis[top]] == 0) break;
			k = now[top] = head[top];
			int mins = maxn;
			for(; k != -1; k = edge[k].next)
			if(edge[k].w && mins > dis[edge[k].v]) mins = dis[edge[k].v];
			++gap[dis[top] = mins+1];
			top = pre[top];
		}
	}
	return ans;
}
int main()
{
	int t, u, v, w;
	scanf("%d", &t);
	for(int _ = 1; _ <= t; ++_)
	{
		init();
		if(!mapping()) puts("No");
		else
		{
			if(SAP(S, T) >= sum) puts("Yes");
			else puts("No");
		}
	}
	return 0;
}

继续加油~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值