zoukankan      html  css  js  c++  java
  • 补 第三场多校杭电 费用流 K Subsequence

    K Subsequence

    这个题目是这个人想吃东西,但是他每次吃的都是他的美味值都必须不递减,可以吃k次,问这个最大的美味值是多少。

    这个是一个比较明显的费用流,建图也很好建,但是呢,这个题目卡spfa费用流,所以要用dij的费用流。

    刚刚是第一种方法,第二种方法就是优化,减少很多边来优化这个复杂度。

    因为每次的美味值都必须不递减,

    所以比如我们给x建边,w[x]=a  后面我们建比a大的w[y]=b 如果后面有比b大的z位置就不建了x,z 之间的边,因为之后y,z会建边,x,z可以通过 xy,yz 来连接。

    所以呢,这样子建边就少了很多条边,

    这个之后还有一次很重要的操作,就是每一个拆点之间要建一条 流量为inf  费用 为 0 的边。

    这个是为了保持图的连通性,比如k点,k点已经被用过一次了,但是k点之前有一个点还没有用,它可以通过k点与另外一个点相连,

    但是k点已经用过了,就不可以再跑了,但是这样是不对的,所以为了保持这个图的连通性,保证答案的正确性,这个k点与它的拆点之间还要连一条边。

    这条边容量应该是inf,因为你不能保证k点之前还有多少个点通过k点和k点之后的点连在一起。

    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <algorithm>
    #include <map>
    #include <queue>
    #include <vector>
    #define inf 0x3f3f3f3f
    using namespace std;
    const int INF = 0x3f3f3f3f;
    const int maxn = 1e5 + 10;
    typedef long long ll;
    struct edge {
        int u, v, c, f, cost;
        edge(int u, int v, int c, int f, int cost) :u(u), v(v), c(c), f(f), cost(cost) {}
    };
    vector<edge>e;
    vector<int>G[maxn];
    int a[maxn];//找增广路每个点的水流量
    int p[maxn];//每次找增广路反向记录路径
    int d[maxn];//SPFA算法的最短路
    int inq[maxn];//SPFA算法是否在队列中
    void init(int n) {
        for (int i = 0; i <= n; i++)G[i].clear();
        e.clear();
    }
    void addedge(int u, int v, int c, int cost) {
        e.push_back(edge(u, v, c, 0, cost));
        e.push_back(edge(v, u, 0, 0, -cost));
        int m = e.size();
        G[u].push_back(m - 2);
        G[v].push_back(m - 1);
    }
    bool bellman(int s, int t, int& flow, long long & cost) {
        memset(d, 0xef, sizeof(d));
        memset(inq, 0, sizeof(inq));
        d[s] = 0; inq[s] = 1;//源点s的距离设为0,标记入队
        p[s] = 0; a[s] = INF;//源点流量为INF(和之前的最大流算法是一样的)
    
        queue<int>q;//Bellman算法和增广路算法同步进行,沿着最短路拓展增广路,得出的解一定是最小费用最大流
        q.push(s);
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            inq[u] = 0;//入队列标记删除
            for (int i = 0; i < G[u].size(); i++) {
                edge & now = e[G[u][i]];
                int v = now.v;
                if (now.c > now.f && d[v] < d[u] + now.cost)
                    //now.c > now.f表示这条路还未流满(和最大流一样)
                    //d[v] > d[u] + e.cost Bellman 算法中边的松弛
                {
                    d[v] = d[u] + now.cost;//Bellman 算法边的松弛
                    p[v] = G[u][i];//反向记录边的编号
                    a[v] = min(a[u], now.c - now.f);//到达v点的水量取决于边剩余的容量和u点的水量
                    if (!inq[v]) { q.push(v); inq[v] = 1; }//Bellman 算法入队
                }
            }
        }
        if (d[t] < 0)return false;//找不到增广路
        flow += a[t];//最大流的值,此函数引用flow这个值,最后可以直接求出flow
        cost += (long long)d[t] * (long long)a[t];//距离乘上到达汇点的流量就是费用
        for (int u = t; u != s; u = e[p[u]].u)//逆向存边
        {
            e[p[u]].f += a[t];//正向边加上流量
            e[p[u] ^ 1].f -= a[t];//反向边减去流量 (和增广路算法一样)
        }
        return true;
    }
    int MincostMaxflow(int s, int t, long long & cost) {
        cost = 0;
        int flow = 0;
        while (bellman(s, t, flow, cost));//由于Bellman函数用的是引用,所以只要一直调用就可以求出flow和cost
        return flow;//返回最大流,cost引用可以直接返回最小费用
    }
    int f[maxn], dp[maxn];
    int main() {
        int w;
        scanf("%d", &w);
        while (w--) {
            int n, k;
            scanf("%d%d", &n, &k);
            for (int i = 1; i <= n; i++) scanf("%d", &f[i]);
            int s1 = 0, s2 = 2 * n + 1, t1 = 2 * n + 2, t2 = 2 * n + 3;
            init(t2);
            addedge(s1, s2, k, 0); addedge(t1, t2, k, 0);
            for (int i = 1; i <= n; i++) {
                addedge(i, i + n, 1, f[i]);
                addedge(i, i + n, inf, 0);
                addedge(s2, i, 1, 0);
                addedge(i + n, t1, 1, 0);
            }
            for (int i = 1; i <= n; i++) {
                int x = inf;
                for (int j = i + 1; j <= n; j++) {
                    if (f[i] <= f[j] && f[j] < x) addedge(i + n, j, 1, 0), x = f[j];
                }
            }
            ll cost = 0;
            MincostMaxflow(s1, t2, cost);
            printf("%lld
    ", cost);
        }
        return 0;
    }
    费用流
  • 相关阅读:
    Vue项目中全局过滤器的使用(格式化时间)
    vue-photo-preview 图片放大功能
    mongoimport导入json文件
    node后台,MongoDB作为数据库,vue前端获取数据并渲染
    JeasyUI,导出Excel
    EasyUI的textbox的disable ,readonly 用法
    EasyUI 中 Combobox里的onChange和onSelect事件的区别
    NullReferenceException 的可恨之处
    最新国家行政区划代码,来自国家统计局2018年底最新数据
    把旧系统迁移到.Net Core 2.0 日记 (20) --使用MiniProfiler for .NET
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/11268178.html
Copyright © 2011-2022 走看看