HDU-5855 Less Time, More profit(最大权闭合图+二分)

题意:

有n个工厂和m个商店,商店已经存在,而工厂需要建造且需要一定的花费和一定的时间,工厂的建造可以同时建,如果建造了指定的工厂,相应的商店会获得一定的收入。给定一个L,求最少需要花费多长时间去建造一些工厂使得商店获得的收入减去工厂的花费>=L,以及在这个最小时间下的最大利润,不能实现的话输出impossible。

思路:

如果没有时间要求和L去求获得的最大利润,就是一个最大权闭合图的裸体,而现在需要求一个最小时间,其实我们二分一下时间就可以了。

代码:

#include <algorithm>  
#include <iostream>
#include <string.h>  
#include <cstdio>  
#include <queue>  
using namespace std;  
const int inf = 0x3f3f3f3f;  
const int maxn = 405;
const int maxm = maxn*maxn;  
const int BAS = 200;  
struct node  
{  
    int v, w, next;  
} edge[maxm];  
int no, head[maxn];  
int t, n, m, L, UP, all, S, T;  
int pay[maxn], ti[maxn];
int pro[maxn];
int bad[maxn];
vector<int> hav[maxn];
queue<int> q;  
int rec[maxn], pre[maxn], block[maxn], dis[maxn];  
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++;  
}   
void reset(int S, int T)  
{   
    memset(dis, 0x3f, sizeof dis);  
    memset(block, 0, sizeof block);  
    q.push(S); dis[S] = 0;  
    while(!q.empty())  
    {  
        int top = q.front(); q.pop();  
        for(int k = head[top]; k != -1; k = edge[k].next)  
        if(dis[edge[k].v] == inf && edge[k].w)  
            dis[edge[k].v] = dis[top]+1, q.push(edge[k].v);  
    }  
}  
int dinic(int S, int T)  
{  
    int ans = 0, flow = inf, top = S;  
    pre[S] = S;  
    reset(S, T);  
    while(dis[T] != inf)  
    {  
        int k, tmp;  
        for(k = head[top]; k != -1; k = edge[k].next)  
        {  
            if(edge[k].w && dis[edge[k].v]==dis[top]+1 &&   
            !block[edge[k].v]) break;  
        }  
        if(k != -1)  
        {  
            tmp = edge[k].v;  
            flow = min(flow, edge[k].w);  
            pre[tmp] = top, rec[tmp] = k;  
            top = tmp;  
            if(top == T)  
            {  
                ans += flow;  
                for(; top != S; top = pre[top])  
                {  
                    edge[rec[top]].w -= flow;  
                    edge[rec[top]^1].w += flow;  
                    if(!edge[rec[top]].w) tmp = top;  
                }  
                top = pre[tmp], flow = inf;  
                for(; top != S; top = pre[top])  
                flow = min(flow, edge[rec[top]].w);  
                top = pre[tmp];  
            }  
        }  
        else  
        {  
            block[top] = 1;  
            top = pre[top];  
            if(block[S]) reset(S, T);  
        }  
    }  
    return ans;  
}
bool mapping()  
{
    scanf("%d %d %d", &n, &m, &L);
    UP = 0;
	for(int i = 1; i <= n; ++i)
	{
		scanf("%d %d", &pay[i], &ti[i]);	
		UP = max(UP, ti[i]);
		add(BAS+i, T, pay[i]);
	}	
    for(int i = 1; i <= m; ++i)  
    {  
        int k, x;
        scanf("%d %d", &pro[i], &k);
		for(int j = 1; j <= k; ++j)
		{
			scanf("%d", &x);
			hav[x].push_back(i);
			add(i, BAS+x, inf);
		}
        add(S, i, pro[i]);
        all += pro[i];
    }  
    return all-dinic(S, T) >= L;
} 
int work(int p)
{
	init(); all = 0;
	memset(bad, 0, sizeof bad);
	for(int i = 1; i <= n; ++i)
	{
		if(ti[i] > p)
		{
			for(int j = 0; j < hav[i].size(); ++j)
			bad[hav[i][j]] = 1;
		}
		else
		{
			add(BAS+i, T, pay[i]);
			for(int j = 0; j < hav[i].size(); ++j)
			add(hav[i][j], BAS+i, inf); 
		}
	}
	for(int i = 1; i <= m; ++i)
	{
		if(bad[i]) continue;
		all += pro[i];
		add(S, i, pro[i]);
	}
	return all-dinic(S, T);
}
int main()  
{  
	scanf("%d", &t);
	for(int _ = 1; _ <= t; ++_)
	{
    	S = BAS*2+1, T = BAS*2+2;
    	for(int i = 0; i <= BAS; ++i) hav[i].clear();
	    all = 0; init(); 
	    if(!mapping()) printf("Case #%d: impossible\n", _); 
	    else
		{
			int mid, l = 0, r = UP;
			while(l <= r)
			{
				mid = (l+r)/2;
				if(work(mid) >= L) r = mid-1;
				else l = mid+1;
			}
			printf("Case #%d: %d %d\n", _, l, work(l));
		}   
	}
    return 0;  
}


继续加油~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值