hdu 5855 Less Time, More profit【二分+最大权闭包--最大流Dinic】

Less Time, More profit

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 293    Accepted Submission(s): 101

Problem Description

The city planners plan to build N plants in the city which has M shops.

Each shop needs products from some plants to make profit of proi units.

Building ith plant needs investment of payi units and it takes ti days.

Two or more plants can be built simultaneously, so that the time for building multiple plants is maximum of their periods(ti).

You should make a plan to make profit of at least L units in the shortest period.

 

 

Input

First line contains T, a number of test cases.

For each test case, there are three integers N, M, L described above.

And there are N lines and each line contains two integers payi, ti(1<= i <= N).

Last there are M lines and for each line, first integer is proi, and there is an integer k and next k integers are index of plants which can produce material to make profit for the shop.

1 <= T <= 30
1 <= N, M <= 200
1≤L,ti≤1000000000
1≤payi,proi≤30000

Output

For each test case, first line contains a line “Case #x: t p”, x is the number of the case, t is the shortest period and p is maximum profit in t hours. You should minimize t first and then maximize p.

If this plan is impossible, you should print “Case #x: impossible”

Sample Input

2

1 1 2

1 5

3 1 1

1 1 3

1 5

3 1 1

Sample Output

Case #1: 5 2

Case #2: impossible

Author

金策工业综合大学(DPRK

Source

2016 Multi-University Training Contest 9

 

题目大意:有t组数据,有n个工厂,有m个商场,有个最低获得利润的限制,建立一个工厂需要花费payi,花费ti天,商场需要对应好多种工厂来供货,只有货全了才能获得对应的利润,问你最少多少天能够获得L以上的利润,例如是多少。


思路:


1、首先对于这个问题如果没有天数的限制,就是一个最大流中最大权闭包的模型,ans=可能获得的收益和-最大流(最大流==最小割).可以考虑为:最大收益=可能获得的收益和-最小建立花费(也就是最小割)。

那么对应建图:

①将各个工厂连入汇点T,其权值为建立工厂的花费,表示建立一个工厂需要对应的花费。

②将源点连入各个商场,其权值为商场能够获得的利润。

③将商场连入对应需要供货的工厂,其权值为INF.


然后跑最大流。ans=可能获得的收益和-最大流


2、那么加上了时间的限制,其实就是多了一个需要枚举的变量,而且这个变量范围比较大,而且满足递增性,考虑二分求解。


3、那么有了一个需要枚举的变量,对应建图的时候多一些限制条件即可。注意在代码实现的时候,ans取可行解,不是取最大解。。。。。

这里感谢yanQval帮我debug............


Ac代码:


#include<stdio.h>
#include<string.h>
#include<queue>
#include<iostream>
using namespace std;
#define INF 1<<30
struct node
{
    int from;
    int to;
    int w;
    int next;
}e[15151515];
int num[350];
int divv[200000];
int cur[200000];
int head[200000];
int earn[2000];
int chose[350][350];
int pay[2000];
int ti[2000];
int n,m,cont,ss,tt;
int ansmoney,kk;
void add(int from,int to,int w)
{
    e[cont].from=from;
    e[cont].to=to;
    e[cont].w=w;
    e[cont].next=head[from];
    head[from]=cont++;
}
void getmap(int mid)
{
    ss=1500;
    tt=1501;
    cont=0;
    memset(head,-1,sizeof(head));
    for(int i=0;i<n;i++)
    {
        if(ti[i]<=mid)
        {
            add(i,tt,pay[i]);
            add(tt,i,0);
        }
    }
    for(int i=0;i<m;i++)
    {
        int flag=1;
        for(int j=0;j<num[i];j++)
        {
            int v=chose[i][j];
            v--;
            if(ti[v]>mid)flag=0;
        }
        if(flag==0)continue;
        add(ss,i+n,earn[i]);
        add(i+n,ss,0);
        for(int j=0;j<num[i];j++)
        {
            int v=chose[i][j];
            v--;
            add(i+n,v,INF);
            add(v,i+n,0);
        }
    }
}
int makedivv()
{
    memset(divv,0,sizeof(divv));
    divv[ss]=1;
    queue<int >s;
    s.push(ss);
    while(!s.empty())
    {
        int u=s.front();
        if(u==tt)return 1;
        s.pop();
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            int w=e[i].w;
            if(w&&divv[v]==0)
            {
                divv[v]=divv[u]+1;
                s.push(v);
            }
        }
    }
    return 0;
}
int Dfs(int u,int maxflow,int tt)
{
    if(u==tt)return maxflow;
    int ret=0;
    for(int &i=cur[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        int w=e[i].w;
        if(w&&divv[v]==divv[u]+1)
        {
            int f=Dfs(v,min(maxflow-ret,w),tt);
            e[i].w-=f;
            e[i^1].w+=f;
            ret+=f;
            if(ret==maxflow)return ret;
        }
    }
    return ret;
}
int Slove(int mid)
{
    getmap(mid);
    int ans=0;
    while(makedivv()==1)
    {
        memcpy(cur,head,sizeof(head));
        ans+=Dfs(ss,INF,tt);
    }
    int sum=0;
    for(int i=0;i<m;i++)
    {
        int flag=1;
        for(int j=0;j<num[i];j++)
        {
            int tmp=chose[i][j];
            tmp--;
            if(ti[tmp]>mid)flag=0;
        }
        if(flag==1)sum+=earn[i];
    }
    sum-=ans;
    if(sum>=kk)return sum;
    else return -1;
}
int main()
{
    int t;
    int kase=0;
    scanf("%d",&t);
    while(t--)
    {
        memset(earn,0,sizeof(earn));
        memset(num,0,sizeof(num));
        memset(chose,0,sizeof(chose));
        memset(pay,0,sizeof(pay));
        memset(ti,0,sizeof(ti));
        scanf("%d%d%d",&n,&m,&kk);
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&pay[i],&ti[i]);
        }
        for(int i=0;i<m;i++)
        {
            scanf("%d",&earn[i]);
            int k;
            scanf("%d",&k);
            num[i]=k;
            for(int j=0;j<k;j++)
            {
                scanf("%d",&chose[i][j]);
            }
        }
        int l=0;
        int r=1000000000;
        int mid;
        int ansday=-1;
        ansmoney=0;
        while(r>=l)
        {
            mid=(l+r)/2;
            int ttt=Slove(mid);
            if(ttt!=-1)
            {
                r=mid-1;
                ansday=mid;
                ansmoney=ttt;
            }
            else
            {
                l=mid+1;
            }
        }
        printf("Case #%d: ",++kase);
        if(ansday!=-1)
        printf("%d %d\n",ansday,ansmoney);
        else printf("impossible\n");
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值