焦作F Modular Production Line 费用流

题目链接

题解:这道题比赛的时候,学弟说是网络流,当时看N这么大,觉得网络流没法做,实际本题通过巧妙的建图,然后离散化。

先说下建图方式,首先每个覆盖区域,只有左右端点,如果我们只用左右端点的话,最多有400个点,所以第一步离散化。每个$i$和$i+1$连一条边流量为K,费用为0的边,每一个覆盖区域从左端点$u$向右端点$v$连一条流量为1,费用为$-w$的边,因为一般算最大费用,就要把边权变成负的算最小,最后取反是最大。对了,右端点$v$要+1,因为这个覆盖区域是从$u->v$,我应该从$v+1$开始流下一个。然后源点向最左端点连一条边流量为K,费用为0的边,汇点从最右端点连条相同的边。跑费用流即可。

为什么可行呢?

首先限制了总流量为K,那么流过离散化后的每个点最大流量是K,比如下面这个例子K=3,做一条纵向切线流量最多为3,保证了答案的正确性。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1000;
const int INF = 1e9;
int dist[maxn];
int pv[maxn],pe[maxn];
struct edge
{
    int to, cap, rev;
    int cost;
    edge(int a, int b, int c, int d)
    {
        to = a, cap = b, cost = c, rev = d;
    }
};
vector<edge> g[maxn];
void addedge(int from,int to,int cap,int cost)
{
    g[from].push_back(edge(to,cap,cost,g[to].size()));
    g[to].push_back(edge(from,0,-cost,g[from].size()-1));
}
int n;
int vis[maxn];
void SPFA(int s, int t)
{
    for(int i = 0; i < maxn; i++) dist[i] = INF;
    memset(vis, 0, sizeof(vis));
    dist[s] = 0, vis[s] = 1;
    queue<int> q;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = 0; i < g[u].size(); i++)
        {
            edge &e = g[u][i];
            if(e.cap > 0 && (dist[e.to] - (dist[u] + e.cost)) > 0)
            {
                pv[e.to] = u, pe[e.to] = i;
                dist[e.to] = dist[u] + e.cost;
                if(!vis[e.to])
                {
                    vis[e.to] = 1;
                    q.push(e.to);
                }
            }
        }
    }
}
int min_cost_flow(int s,int t,int f,int& max_flow)
{
    int ret = 0.0;
    while(f>0)
    {
        SPFA(s, t);
        if(dist[t] == INF) return ret;///同一目的地,每次增广路都是最小费用
        ///当所有边的流量都流净后,即没有残余网络,返回。
        int d = f;
        for(int v=t;v!=s;v=pv[v])
        {
            d = min(d,g[pv[v]][pe[v]].cap);
        }
        f -= d;
        max_flow += d;
      //  printf("%d\n", ret);
        ret += (int)d*dist[t]; ///走一单位就消耗dist[t]
        for(int v=t;v!=s;v=pv[v])
        {
            edge &e = g[pv[v]][pe[v]];
            e.cap -= d;
            g[v][e.rev].cap += d;
        }
    }
    return ret;
}
int u[maxn], v[maxn], w[maxn], low[maxn];
int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        int K, M, tmpn;
        scanf("%d %d %d", &tmpn, &K, &M);
        int cnt = 0;
        for(int i = 1; i <= M; i++)
        {
           scanf("%d %d %d", &u[i], &v[i], &w[i]);
           v[i]++; ///v[i]是我所占用的,要直接流到v[i]+1
           low[cnt++] = u[i];
           low[cnt++] = v[i];
        }
        sort(low, low + cnt);
        cnt = unique(low, low + cnt) - low;
        for(int i = 1; i <= M; i++)
        {
            u[i] = lower_bound(low, low + cnt, u[i]) - low;
            v[i] = lower_bound(low, low + cnt, v[i]) - low;
        }
        for(int i = 1; i <= M; i++)
        {
      //      printf("%d %d\n", u[i], v[i]);
        }
      //  printf("%d\n",cnt);
        for(int i = 0; i < cnt + 10; i++) g[i].clear();
        for(int i = 0; i < cnt - 1; i++)
        {
            addedge(i, i + 1, K, 0);
        }
        for(int i = 1; i <= M; i++)
        {
            addedge(u[i], v[i], 1, -w[i]);
        }
        addedge(cnt + 1, 0, K, 0);
        addedge(cnt - 1, cnt + 2, K, 0);
        int maxflow = 0;
        int ans = min_cost_flow(cnt + 1, cnt + 2, INF, maxflow);
        printf("%d\n", -ans);
    }
    return 0;
}
Code

 

posted @ 2018-09-19 20:57  汪汪鱼  阅读(170)  评论(0编辑  收藏  举报