zoukankan      html  css  js  c++  java
  • 2016 Multi-University Training Contest 9

     

    solved 1/13

    2016 Multi-University Training Contest 9

    二分+最大权闭合图 Less Time, More profit(BH)

    题意就是有n个工厂,m个商店 
    每个工厂有建造时间ti,花费payi 
    每个商店和k个工厂有关,如果这k个工厂都建造了,那么能获利proi 
    问你求收益(∑pro−∑pay)≥L时,首先满足时间t最小,其次是收益p最大

    首先二分时间的答案,然后看那些工厂能建造,然后工厂是花费,商店是收益,并且要与商店有关的工厂都建造了,才能获利,所以这是一个最大权闭合图的问题。

    关于最大权闭合图:岐哥的博客

    闭合图就是闭合图中的点的后继也在这个闭合图中。而闭合图是反映事物间必要条件的关系的,也就是说一个点是它后继的必要条件。也就是说要有这个点,它的后继点也要都存在。

    最大权闭合图:

    建图:构造一个源点S,汇点T。我们将S与所有权值为正的点连一条容量为其权值的边,将所有权值为负的点与T连一条容量为其权值的绝对值的边,原来的边将其容量定为正无穷。

    求解:最大权闭合图=正的点权和-最小割=正的点权和-最大流。

    时间复杂度为.

    代码:

    #include <bits/stdc++.h>
    
    const int N = 200 + 5;
    const int INF = 0x3f3f3f3f;
    int pay[N], t[N];
    int pro[N];
    std::vector<int> need[N];
    int n, m;
    int L;
    
    /*
       *最大流之Dinic算法:不停地构造层次图,然后用阻塞流增广。
       *时间复杂度为 O(n^2*m)。
       *如果容量为1,复杂度为 O(min(n^(2/3), m^(1/2)*m)。
       *对于二分图最大匹配这样的特殊图,复杂度为 O(n^(1/2)*m)
    */
    struct Max_Flow {
        struct Edge {
            int from, to, cap, flow;
        };
        std::vector<Edge> edges;
        std::vector<int> G[2*N]; //保存每个结点的弧在edges里的序号
        int level[2*N], cur[2*N]; //结点在层次图中的等级。结点当前弧下标。
        int n, m, s, t;
        //初始化顶点个数n,边的个数m在加边时统计,s和t分别为源点和汇点
        void init(int n) {
            this->n = n;
            for (int i=0; i<=n; ++i) {
                G[i].clear ();
            }
            edges.clear ();
        }
        void add_edge(int from, int to, int cap) {
            edges.push_back ((Edge) {from, to, cap, 0});
            edges.push_back ((Edge) {to, from, 0, 0});
            m = edges.size ();
            G[from].push_back (m - 2);
            G[to].push_back (m - 1);
        }
        bool BFS() {
            std::fill (level, level+1+n, -1);
            std::queue<int> que;
            level[s] = 0; que.push (s);
            while (!que.empty ()) {
                int u = que.front (); que.pop ();
                for (int i=0; i<G[u].size (); ++i) {
                    Edge &e = edges[G[u][i]];
                    if (level[e.to] == -1 && e.cap > e.flow) {
                        level[e.to] = level[u] + 1;
                        que.push (e.to);
                    }
                }
            }
            return level[t] != -1;
        }
        int DFS(int u, int a) {
            if (u == t || a == 0) {
                return a; //a表示当前为止所有弧的最小残量
            }
            int flow = 0, f;
            for (int &i=cur[u]; i<G[u].size (); ++i) {
                Edge &e = edges[G[u][i]];
                if (level[u] + 1 == level[e.to]
                && (f = DFS (e.to, std::min (a, e.cap - e.flow))) > 0) {
                    e.flow += f;
                    edges[G[u][i]^1].flow -= f;
                    flow += f; a -= f;
                    if (a == 0) {
                        break;
                    }
                }
            }
            return flow;
        }
        int Dinic(int s, int t) {
            this->s = s; this->t = t;
            int flow = 0;
            while (BFS ())  {
                std::fill (cur, cur+1+n, 0);
                flow += DFS (s, INF);
            }
            return flow;
        }
    }max_flow;
    
    int check(int max_t) {
        int S = 0, T = n + m + 1;
        int sum = 0;
        max_flow.init (n+m+2);
        for (int i=1; i<=m; ++i) {
            max_flow.add_edge (S, i, pro[i]);
            bool flag = true;
            for (int j: need[i]) {
                if (t[j] > max_t) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                for (int j: need[i]) {
                    max_flow.add_edge (i, m+j, INF);
                }
                sum += pro[i];
            }
        }
        for (int i=1; i<=n; ++i) {
            if (t[i] <= max_t)
                max_flow.add_edge (m+i, T, pay[i]);  //abs (-pay[i])
        }
        int min_cut = max_flow.Dinic (S, T);
        if (sum - min_cut >= L)
            return sum - min_cut;
        else
            return -1;
    }
    
    void solve(int cas) {
        printf ("Case #%d: ", cas);
        int best_t = INF, best_p = 0;
        int l = 1, r = 1000000000;
        while (l <= r) {
            int mid = l + r >> 1;
            int res = check (mid);
            if (res != -1) {
                best_t = std::min (best_t, mid);
                best_p = res;
                r = mid - 1;
            } else
                l = mid + 1;
        }
        if (best_t < INF)
            printf ("%d %d
    ", best_t, best_p);
        else
            puts ("impossible");
    }
    
    int main() {
        int T;
        scanf ("%d", &T);
        for (int cas=1; cas<=T; ++cas) {
            scanf ("%d%d%d", &n, &m, &L);
            for (int i=1; i<=n; ++i) {
                scanf ("%d%d", pay+i, t+i);
            }
            for (int i=1; i<=m; ++i) {
                scanf ("%d", pro+i);
                need[i].clear ();
                int k;
                scanf ("%d", &k);
                while (k--) {
                    int id;
                    scanf ("%d", &id);
                    need[i].push_back (id);
                }
            }
            solve (cas);
        }
        return 0;
    }
    

    还有贪心的做法,时间复杂度解释

    代码:

    #include <bits/stdc++.h>
    
    const int N = 200 + 5;
    const int INF = 0x3f3f3f3f;
    
    struct Plant {
        int pay, t;
    }p[N];
    std::vector<int> id[N];
    
    struct Shop {
        int pro;
        std::vector<int> need;
        int pay;
        int max_t;
        int done;
    }s[N];
    
    int n, m;
    int L;
    
    bool vis_p[N], vis_s[N];
    
    int check(int max_t) {
        memset (vis_p, false, sizeof (vis_p));
        memset (vis_s, false, sizeof (vis_s));
        for (int i=0; i<m; ++i)
            s[i].done = 0;
        int ret = -INF, tmp = 0;
        for (; ;) {
            int max_val = -INF, k = -1;
            for (int i=0; i<m; ++i) {
                if (!vis_s[i] && s[i].max_t <= max_t) {
                    if (max_val < s[i].pro - s[i].pay + s[i].done) {
                        max_val = s[i].pro - s[i].pay + s[i].done;
                        k = i;
                    }
                }
            }
            if (max_val == -INF)
                break;
            vis_s[k] = true;  //max m times
            tmp += max_val;
            ret = std::max (ret, tmp);
            for (int j: s[k].need) {
                if (!vis_p[j]) {
                    vis_p[j] = true;
                    for (int l: id[j]) {
                        if (!vis_s[l]) {
                            s[l].done += p[j].pay;
                        }
                    }
                }
            }
        }
        return ret >= L ? ret : -1;
    }
    
    void solve(int cas) {
        printf ("Case #%d: ", cas);
        int best_t = INF, best_p = 0;
        int l = 1, r = 1000000000;
        while (l <= r) {
            int mid = l + r >> 1;
            int res = check (mid);
            if (res != -1) {
                best_t = std::min (best_t, mid);
                best_p = res;
                r = mid - 1;
            } else
                l = mid + 1;
        }
        if (best_t < INF)
            printf ("%d %d
    ", best_t, best_p);
        else
            puts ("impossible");
    }
    
    int main() {
        int T;
        scanf ("%d", &T);
        for (int cas=1; cas<=T; ++cas) {
            scanf ("%d%d%d", &n, &m, &L);
            for (int i=0; i<n; ++i) {
                scanf ("%d%d", &p[i].pay, &p[i].t);
                id[i].clear ();
            }
            for (int i=0; i<m; ++i) {
                scanf ("%d", &s[i].pro);
                s[i].need.clear ();
                s[i].max_t = -1;
                s[i].pay = 0;
                int k;
                scanf ("%d", &k);
                while (k--) {
                    int j;
                    scanf ("%d", &j);
                    j--;
                    if (p[j].t > s[i].max_t)
                        s[i].max_t = p[j].t;
                    s[i].pay += p[j].pay;
                    s[i].need.push_back (j);
                    id[j].push_back (i);
                }
            }
            solve (cas);
        }
        return 0;
    }
    

      

     

  • 相关阅读:
    mysql中InnoDB存储引擎的行锁和表锁
    阿里云出海 埃森哲护航
    阿里云出海 埃森哲护航
    阿里云出海 埃森哲护航
    阿里云出海 埃森哲护航
    Python开发简单爬虫
    Python开发简单爬虫
    Python开发简单爬虫
    Python开发简单爬虫
    问大家一个问题,如何用1万元创业,每天利润达到500元?
  • 原文地址:https://www.cnblogs.com/NEVERSTOPAC/p/5777833.html
Copyright © 2011-2022 走看看