USC oj 1611 XX‘s dream(网络流)

最大权闭合子图介绍:

题目链接:
http://61.187.179.71:9988/problem.php?id=1611

题目大意:XX有很多梦想,完成梦想能获得价值X,但是一个梦想需要许多种努力,每种努力可能需要消耗价值也可能得到价值,问在获得最大价值的条件下,最多可完成多少种梦想

解题思路:

最大权闭合子图模板题,梦想和源点连边,可得到价值的努力也和源点连边,会消耗价值的努力和汇点连边,跑一遍网络流得到最小割,用所有正价值的和减去最小割即可。

但是这道题还要求最多能完成多少个梦想,一般情况下可能有多种不同的割边方式能得到我们需求的最大价值,所以我们求得的不一定是最大的。

这里有一个神奇的边权放大法,将所有与源点汇点相连的边乘以一个较大的常数(至少要大于图中梦想的点数),然后再给所有的梦想与源点相连的边的权值加1,这样最后得到的最大价值再除以我们乘的常数就是我们需求的价值,最多的梦想就是源点可以到达的梦想个数,直接在残余网络中dfs一遍统计能到达的梦想点就可以了(能到达的点就是我们建的边和反向边中权值大于0的)。

伪AC代码:
//oj上数据有问题。。(别问我为什么知道,标称都跑不过去,标称跑的结果和我一样。。。)

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <stack>
#include <map>
#include <cstring>
#include <string>
#include <queue>
typedef long long LL;
const LL INF=1e15;
using namespace std;

const LL MAX_V=2100;
struct edge
{
    LL to,cap,rev;

    edge(LL a,LL b,LL c)
    {
        to = a;
        cap =b;
        rev = c;
    }

};

vector<edge> g[MAX_V];
LL level[MAX_V];
LL iter[MAX_V];

bool used[MAX_V];

void add_edge(LL from,LL to,LL cap )
{
    //cout<<from<<"--¡·"<<to<< " "<<cap<<endl;
    g[from].push_back(edge(to,cap,g[to].size()));
    g[to].push_back(edge(from,0,g[from].size()-1));
}

void bfs(LL s)
{
    memset(level,-1,sizeof(level));
    queue<LL> que;
    level[s]=0;
    que.push(s);
    while(!que.empty())
    {
        LL v=que.front();
        que.pop();
        for(LL i=0; i<g[v].size(); i++)
        {
            edge &e=g[v][i];
            if(e.cap>0&&level[e.to]<0)
            {
                level[e.to]=level[v]+1;
                que.push(e.to);
            }
        }
    }

}
LL dfs(LL v,LL t,LL f)
{
    if(v==t) return f;
    for(LL &i=iter[v]; i<g[v].size(); i++)
    {
        edge &e = g[v][i];
        if(e.cap >0&&level[v]<level[e.to])
        {
            LL d = dfs(e.to,t,min(f,e.cap));
            if(d>0)
            {
                e.cap-=d;
                g[e.to][e.rev].cap +=d;
                return d;
            }
        }
    }
    return 0;
}

LL max_flow(LL s,LL t)
{
    LL flow =0;
    for(;;)
    {
        bfs(s);
        if(level[t]<0) return flow;
        memset(iter,0,sizeof(iter));
        LL f;
        while((f=dfs(s,t,INF))>0) flow+=f;

    }
}
bool vis[MAX_V];

void dfs1(LL v)
{
    vis[v]=true;
    for(LL i=0; i<g[v].size(); i++)
    {
        if(g[v][i].cap>0&&(!vis[g[v][i].to])) {
                //cout<<v<<"-->"<<g[v][i].to<<endl;
               // cout<<"cap = "<<g[v][i].cap<<endl;
                dfs1(g[v][i].to);
        }
    }

}


int main()
{
    //cout<<INF<<endl;
    LL m,n;
    while(scanf("%I64d %I64d",&n,&m)!=EOF)
    {
        for(LL i=0; i<MAX_V; i++)
            g[i].clear();
        LL total=0;
        for(LL i=1; i<=n; i++)
        {
            LL dream;
            scanf("%I64d",&dream);
            add_edge(0,i,dream*10000+1);
            total+=dream*10000+1;
        }
        for(LL i=1; i<=m; i++)
        {
            LL tr;
            scanf("%I64d",&tr);
            if(tr>=0)
            {
                add_edge(0,n+i,tr*10000);
                total+=tr*10000;
            }
            else add_edge(n+i,n+m+1,(-tr)*10000);
        }

        LL k;

        for(LL i=1; i<=n; i++)
        {
            scanf("%I64d",&k);
            for(LL j=1; j<=k; j++)
            {
                LL t;
                scanf("%I64d",&t);
                add_edge(i,n+t,INF);
            }
        }

        LL ans2=0;



        LL ans =(total- max_flow(0,n+m+1))/10000;
        //cout<<"qq"<<endl;
        memset(vis,0,sizeof(vis));
        dfs1(0);


        for(LL i=1;i<=n;i++)
            if(vis[i]) ans2++;

        printf("%I64d %I64d\n",ans,ans2);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值