hdu5855(2016多校第9场,二分,最大权闭合图,最大流)

202 篇文章 0 订阅
123 篇文章 0 订阅

Less Time, More profit

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


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
1L,ti1000000000
1payi,proi30000
 

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


闭合图:一个有向图的子点集,使其中的点的出边都指回集合中的点,则称此为闭合图。

最大权闭合图:给每个点赋上点权,则权和最大的闭合图,为最大权闭合图。

算法(求最大权闭合子图):

(1)新增源点和汇点s,t

(2)对于原图中的边u->v,增加一条u->v的容量为无穷大的边

(3)对于点u,如果w[u]>0,增加一条s->u的容量为w[u]的边;否则增加一条u->v的容量为-w[u]的边


定义一个割划分出的S集合为一个解,那么割集的容量之和就是(未被选的A集合中的顶点的权值 + 被选的B集合中的顶点的权值),记为Cut。A集合中所有顶点的权值之和记为Total,那么Total - Cut就是(被选的A集合中的顶点的权值 - 被选的B集合中的顶点的权值),即为我们的目标函数,记为A。要想最大化目标函数A,就要尽可能使Cut小,Total是固定值,所以目标函数A取得最大值的时候,Cut最小,即为最小割。


用途:闭合图的性质恰好反映了事件之间的必要条件的关系:一个事件发生,它需要的所有前提都要发生。



所以这一题是一样的思路,对应的去建边就好了。


#include <iostream>
#include<string.h>
#include<vector>
#include<queue>
#include<algorithm>
#include<stdio.h>
#include<math.h>
#include<map>
#include<stdlib.h>
#include<time.h>
using namespace std;
typedef long long ll;
struct pl
{
    int pay,t;
} a[210];
struct data
{
    int pro;
    int n;
    int need[210];
} b[210];
const   int oo=1e9;/**oo 表示无穷大*/
const  int mm=111111111;/**mm 表示边的最大数量,记住要是原图的两倍,在加边的时候都是双向的*/
const  int mn=2010;/**mn 表示点的最大数量*/
int node,src,dest,edge;/**node 表示节点数,src 表示源点,dest 表示汇点,edge 统计边数*/
int ver[mm],flow[mm],nex[mm];
int head[mn],work[mn],dis[mn],q[mn];
void prepare(int _node, int _src,int _dest)
{
    node=_node,src=_src,dest=_dest;
    for(int i=0; i<=node; ++i)head[i]=-1;
    edge=0;
}
void addedge( int u,  int v,  int c)
{
    ver[edge]=v,flow[edge]=c,nex[edge]=head[u],head[u]=edge++;
    ver[edge]=u,flow[edge]=0,nex[edge]=head[v],head[v]=edge++;
}
bool Dinic_bfs()
{
    int i,u,v,l,r=0;
    for(i=0; i<node; ++i)dis[i]=-1;
    dis[q[r++]=src]=0;
    for(l=0; l<r; ++l)
        for(i=head[u=q[l]]; i>=0; i=nex[i])
            if(flow[i]&&dis[v=ver[i]]<0)
            {
                dis[q[r++]=v]=dis[u]+1;
                if(v==dest)  return 1;
            }
    return 0;
}
int Dinic_dfs(  int u, int exp)
{
    if(u==dest)  return exp;
    for(  int &i=work[u],v,tmp; i>=0; i=nex[i])
        if(flow[i]&&dis[v=ver[i]]==dis[u]+1&&(tmp=Dinic_dfs(v,min(exp,flow[i])))>0)
        {
            flow[i]-=tmp;
            flow[i^1]+=tmp;
            return tmp;
        }
    return 0;
}
int Dinic_flow()
{
    int i,ret=0,delta;
    while(Dinic_bfs())
    {
        for(i=0; i<node; ++i)work[i]=head[i];
        while((delta=Dinic_dfs(src,oo)))ret+=delta;
    }
    return ret;
}
int main()
{
    int t,o=1;
    scanf("%d",&t);
    while(t--)
    {
        int n,m,L;
        scanf("%d%d%d",&n,&m,&L);
        prepare(n+m+2,0,n+m+1);
        int maxt=0;
        for(int i=1; i<=n; i++)
            scanf("%d%d",&a[i].pay,&a[i].t),maxt=max(maxt,a[i].t);
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d",&b[i].pro,&b[i].n);
            for(int j=0; j<b[i].n; j++)
                scanf("%d",&b[i].need[j]);
        }
        int l=0,r=maxt+1,mid;
        int ans=-1,ansum=0;
        while(l<=r)
        {
            mid=(l+r)/2;
            prepare(n+m+2,0,n+m+1);
            for(int i=1; i<=n; i++)
                if(mid>=a[i].t)
                    addedge(0,i,a[i].pay);
                else addedge(0,i,oo);
            int sum=0;
            for(int i=1; i<=m; i++)
            {
                for(int j=0; j<b[i].n; j++)
                    addedge(b[i].need[j],i+n,oo);
                addedge(i+n,n+m+1,b[i].pro);
                sum+=b[i].pro;
            }
            int ss=Dinic_flow();
            if(sum-ss>=L)
                ans=mid,ansum=sum-ss,r=mid-1;
            else l=mid+1;
        }
        printf("Case #%d: ",o++);
        if(ans!=-1)
            printf("%d %d\n",ans,ansum);
        else
            puts("impossible");
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值