HDU5855 Less Time, More profit(最大权闭合图)

来源:互联网 发布:linux终端命令大全 编辑:程序博客网 时间:2024/04/28 16:09

题意

N个工厂,给出每个工厂建成所需时间t及花费pay(可同时建)。M个商店,在给定的几个工厂建好的前提下,每个商店开张可以获利pro(一次性)。求获利L最少需要的天数T,以及T天的最大获利P


分析

一个很明显的最大权闭合图,商店连接几个工厂,商店点权为pro ,工厂点权为pay ,然后就是求这个图的最大权闭合图,转化成最大流来解。

源点到商店建一条边流量为 pro,工厂到汇点建一条边流量为 pay,商店到获利所需工厂建一条边流量为 inf,商店总收益 减去 最小割(最大流) 即答案。

因为求获利L最少需要的天数T ,我们可以从小到大枚举所给时间,当获利大于等于L ,则为答案。
有些工厂因小于时间 T 未建成,则向汇点连流量为 inf 的边,则可使不满足条件的商店的 pro 全部流向汇点,然后商店总收益即可减去这部分不合法的 pro


代码

#include<iostream>#include<algorithm>#include<cstdio>#include<cmath>#include<cstring>#include<string>#include<vector>#include<cctype>#include<set>#include<map>#include<queue>#include<stack>#include<iomanip>#include<sstream>#include<limits>#define ll long long#define inf 0x3f3f3f3fusing namespace std;const int maxn = 1000;const ll MOD = 1000000007;const double EPS = 1e-10;const double Pi = acos(-1.0);#define INF 0x7fffffffstruct Edge{    int to , cap,rev;  // rev反向边};vector<Edge>G[maxn];int level[maxn],iter[maxn];  // 顶点到源点的标号   当前弧void add_edge(int from,int to,int cap) //cap容量{    G[from].push_back((Edge){to,cap,G[to].size()});    G[to].push_back((Edge){from,0,G[from].size()-1 });}void bfs(int s) // 分层{    memset(level,-1,sizeof(level));    queue<int>que;    level[s] = 0;    que.push(s);    while(!que.empty())    {        int v = que.front();que.pop();        for(int 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);            }        }    }}int dfs(int v, int t, int f) //寻增广路{    if (v==t) return f;    for(int &i = iter[v]; i < G[v].size(); i++)    {        Edge &e = G[v][i];        if (e.cap > 0 && level[v] < level[e.to])        {            int 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;}int max_flow(int s, int t) // s到t最大流{    int flow = 0;    for(;;)    {        bfs(s);        if (level[t] < 0) return flow;        memset(iter,0,sizeof(iter));        int f;        while((f = dfs(s,t,INF)) > 0) { flow += f;}    }}int pay[300],ti[300],pro[300],tt[300];vector<int> shop[205];int main(){#ifdef LOCAL    freopen("C:\\Users\\lanjiaming\\Desktop\\acm\\in.txt","r",stdin);    //freopen("output.txt","w",stdout);#endif//ios_base::sync_with_stdio(0);    int T,kase = 0;    scanf("%d",&T);    while(T--)    {        int n,m,l;        scanf("%d%d%d",&n,&m,&l);        for(int i = 1; i <= n; i++)        {            scanf("%d%d",&pay[i],&ti[i]);            tt[i] = ti[i];        }        for(int i = 1; i <= m; i++)            {                int k;                scanf("%d%d",&pro[i],&k);                shop[i].clear();                for(int j = 0; j < k; j++)                {                    int x;                    scanf("%d",&x);                    shop[i].push_back(x);                }            }        sort(tt+1,tt+n+1);        bool ko = true;        for(int i = 1; i <= n; i++)        {            if (tt[i] == tt[i-1]) continue;            int t = tt[i];            int S = 0, T = n+m+1;            for(int j= 0; j <= T; j++) G[j].clear();   //重新建边            int sum = 0;            for(int j = 1; j <= m; j++)            {                add_edge(S,j,pro[j]);  //源点到商店                sum += pro[j];                for(int k = 0; k < shop[j].size(); k++)                    add_edge(j,shop[j][k]+m,inf);   //商店到工厂            }            for(int j = 1; j <= n; j++)      //工厂到汇点                if (t >= ti[j]) add_edge(j+m,T,pay[j]);                else add_edge(j+m,T,inf);            int ans = sum - max_flow(S,T);            if (ans >= l)            {               printf("Case #%d: %d %d\n",++kase,t,ans);               ko = false;               break;            }        }        if(ko) printf("Case #%d: impossible\n",++kase);    }    return 0;}
0 0
原创粉丝点击