HDU5855(最大权闭合图构图技巧+裸的最大流)

题意:给了n个工厂,m个商店,每个商店只有在要求的工厂都建好后才能获得利润,而建一个工厂需要花费和时间,现在问你能获得利润至少为l的最短时间是多少,在这个时间下的最大利润是多少;

题解:因为每一个商店盈利都必须有一些工厂修好。这就是一个闭合图的暗示!

闭合图论文以及证明

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

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

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

(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<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f;
const ll INFF=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double pi=acos(-1.0);
const double eps=1e-9;
int level[201010];
int iter[201010];
struct node
{
    int x,y,c,rev;
};
vector<node>vec[201010];
void add_edge(int x,int y,int c)
{
    node p1,p2;
    p1.y=y,p1.c=c,p1.rev=vec[y].size();
    vec[x].push_back(p1);
    p2.y=x,p2.c=0,p2.rev=vec[x].size()-1;
    vec[y].push_back(p2);
}
void bfs(int s)
{
    memset(level,-1,sizeof(level));
    queue<int>q;
    level[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int v=q.front();
        q.pop();
        for(int i=0;i<vec[v].size();i++)
        {
            node p=vec[v][i];
            if(p.c>0&&level[p.y]<0)
            {
                level[p.y]=level[v]+1;
                q.push(p.y);
            }
        }
    }
}
int dfs(int v,int t,int f)
{
    if(v==t)
        return f;
    for(int &i=iter[v];i<vec[v].size();i++)
    {
        node &p=vec[v][i];
        if(p.c>0&&level[v]<level[p.y])
        {
            int d=dfs(p.y,t,min(f,p.c));
            if(d>0)
            {
                p.c-=d;
                vec[p.y][p.rev].c+=d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s,int t)
{
    int ans=0;
    for(;;)
    {
        bfs(s);
        if(level[t]<0)
            return ans;
        memset(iter,0,sizeof(iter));
        int f;
        while((f=dfs(s,t,INF))>0)
            ans+=f;
    }
}
int cost[205],time[205];
int GET[205],num[205],dic[205][205];
int main()
{
    int t;scanf("%d",&t);
    for(int vv=1;vv<=t;vv++)
    {
        int n,m,L;scanf("%d%d%d",&n,&m,&L);
        for(int i=1;i<=n;i++)scanf("%d%d",&cost[i],&time[i]);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&GET[i],&num[i]);
            for(int j=1;j<=num[i];j++)
                scanf("%d",&dic[i][j]);
        }
        int l=0,r=INF;
        int anst=-1,ansg=0;
        while(r>=l)
        {
            int mid=(l+r)/2;
            for(int i=0;i<=n+m+10;i++)vec[i].clear();
            for(int i=1;i<=n;i++)
            {
                if(mid>=time[i])add_edge(0,i,cost[i]);
                else add_edge(0,i,INF);
            }
            int sum=0;
            for(int i=1;i<=m;i++)
            {
                for(int j=1;j<=num[i];j++)
                    add_edge(dic[i][j],i+n,INF);
                add_edge(i+n,n+m+1,GET[i]);
                sum+=GET[i];
            }
            int ss=max_flow(0,n+m+1);
            if(sum-ss>=L)
            {
                anst=mid;
                ansg=sum-ss;
                r=mid-1;
            }
            else
            {
                l=mid+1;
            }
        }
        printf("Case #%d: ",vv);
        if(anst!=-1)
            printf("%d %d\n",anst,ansg);
        else
            puts("impossible");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值