【luogu 2050】【NOI2012】美食节(费用流)

【题目大意】n种菜,每种有pi份,有m位厨师,第i位厨师做第j道菜的时间是cij,求将所有菜做完花费的最小时间

【题目分析】

【相对暴力】

直接进行拆点,类似修车
(其实就是一样的
但是问题在于如果直接拆的话点数过多,就会TLE
于是就需要想办法进行优化

(建议不熟的话先把暴力写会)

// luogu-judger-enable-o2
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cstring>
#define ll int
using namespace std;
const int MAXM = 113234;
const int INF = 2e9 + 7;
typedef pair<int, int> P;
struct note
{
    int to;
    int nt;
    int rev;
    ll cost;
    ll cal;
};
int tn, tm, tk, sum;
int p[MAXM], a[200][200];
void read()
{
    scanf("%d%d", &tn, &tm);
    for (int i = 1; i <= tn; i++)
        scanf("%d", &p[i]), sum += p[i];
    for (int i = 1; i <= tn; i++)
        for (int j = 1; j <= tm; j++)
            scanf("%d", &a[i][j]);
}
struct edge
{
    note arr[7000000];
    int  st[MAXM];
    ll   dis[MAXM];
    ll   h[MAXM];
    int  cur[MAXM];
    int  depth[MAXM];
    int  pre[MAXM];
    int  pree[MAXM];
    int  top;
    int n, m, s, t, siz, k;
    edge()
    {
        memset(st, -1, sizeof(st));
        memset(depth, -1, sizeof(depth));
        memset(dis, -1, sizeof(dis));
        top = 0;
    }
    void init()
    {
        memset(st, -1, sizeof(st));
        memset(depth, -1, sizeof(depth));
        memset(dis, -1, sizeof(dis));
        top = 0;

    }
    void build(int ts, int tt)
    {
        s = ts, t = tt, siz = tt;
        n = tn; m = tm;
        for (int i = 1; i <= n; i++)
            add(s, i, p[i], 0);
        for (int i = 1; i <= n; i++)
        {
                for (int j1 = 1; j1 <= m; j1++)
                    for (int k = 1; k <= sum; k++)
                    {
                        add(i, n + (j1 - 1) * sum + k, 1, a[i][j1] * k);
                       
                    }
        }
        for (int i = n + 1; i < t; i++)
            add(i, t, 1, 0);
    }
    bool dep()
    {
        queue<int> q;
        q.push(s);
        memset(depth, -1, sizeof(depth));
        depth[s] = 0;
        while (!q.empty())
        {
            int v = q.front(); q.pop();
            for (int i = st[v]; i != -1; i = arr[i].nt)
            {
                int to = arr[i].to;
                if (!arr[i].cal)
                    continue;
                if (depth[to] != -1)
                    continue;
                depth[to] = depth[v] + 1;
                q.push(to);
            }
        }
        return (depth[t] != -1);
    }
    void add(int x, int y, ll z, ll c)
    {
        top++; arr[top] = { y,st[x],top + 1,c,z }; st[x] = top;
        top++; arr[top] = { x,st[y],top - 1,-c,0 }; st[y] = top;
    }
    ll dfs(int now, ll val)
    {
        if (now == t || !val)
            return val;
        ll flow = 0;
        for (int& i = cur[now]; i != -1; i = arr[i].nt)
        {
            int to = arr[i].to;
            if (depth[to] != depth[now] + 1)
                continue;
            ll f = dfs(to, min(arr[i].cal, val));
            if (!f || !arr[i].cal)
                continue;
            flow += f;
            arr[i].cal -= f;
            arr[arr[i].rev].cal += f;
            val -= f;
            if (!val)
                return flow;
        }
        return flow;
    }
    ll dinic()
    {
        ll flow = 0;
        ll f;
        while (dep())
        {
            for (int i = 0; i <= siz; i++)
                //这里
                cur[i] = st[i];
            while (f = dfs(s, INF))
                flow += f;
        }
        return flow;
    }
    ll min_cost(ll f)
    {
        ll res = 0;
        while (f > 0)
        {
            fill(dis, dis + 1 + siz, INF);
            priority_queue<P, vector<P>, greater<P>> q;
            dis[s] = 0;
            q.push(P(0, s));
            while (!q.empty())
            {
                P p = q.top(); q.pop();
                int v = p.second;
                if (dis[v] < p.first) continue;
                for (int i = st[v]; i != -1; i = arr[i].nt)
                {
                    note& e = arr[i];
                    if (e.cal > 0 && dis[e.to] > dis[v] + e.cost + h[v] - h[e.to])
                    {
                        dis[e.to] = dis[v] + e.cost + h[v] - h[e.to];
                        pre[e.to] = v;
                        pree[e.to] = i;
                        q.push(P(dis[e.to], e.to));
                    }
                }
            }
            if (dis[t] == INF)    return -1;
            for (int i = 0; i <= siz; i++)
                //这里改了
                h[i] += dis[i];
            ll d = f;
            //printf("2\n");
            for (int i = t; i != s; i = pre[i])
            {
                d = min(d, arr[pree[i]].cal);
            }
            f -= d;
            res += d * h[t];
            for (int i = t; i != s; i = pre[i])
            {
                arr[pree[i]].cal -= d;;
                int rev = arr[pree[i]].rev;
                arr[rev].cal += d;
            }
            //printf("3\n");
        }
        return res;
    }
};
edge road, new_road;
edge tmp;
int main()
{
    read();
    road.init();
    road.build(0, tn + sum * tm + 1);
    printf("%d", road.min_cost(sum));
    return 0;
}

【动态加边】

先思考每次遍历的时候会用到哪些边
找一次增广路最多只能找到一条流量为1的增广路
(因为最后连接终点的边流量均为1)
所以说最开始直接把所有的点和边都建好会消耗大量时间

所以我们最初只建完成倒数第一件食物的那一层边

将寻找增广路的过程拆开来看,观察每次查找到增广路之后
被更新的那条边就已经被使用过了,然后将相对应的边加进去

也不是那条边,而是那条边对应的那名厨师,在他的后面加边
说明这名厨师已经在做一道菜了,如果想让他做另外一道菜
就需要排在下一位了

// luogu-judger-enable-o2
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cstring>
#define ll int
using namespace std;
const int MAXM = 113234;
const int INF = 2e9 + 7;
typedef pair<int, int> P;
struct note
{
    int to;
    int nt;
    int rev;
    ll cost;
    ll cal;
};
int tn, tm, tk, sum;
int p[MAXM], a[200][200];
int ha[MAXM], co[MAXM];
void read()
{
    scanf("%d%d", &tn, &tm);
    for (int i = 1; i <= tn; i++)
        scanf("%d", &p[i]), sum += p[i];
    for (int i = 1; i <= tn; i++)
        for (int j = 1; j <= tm; j++)
            scanf("%d", &a[i][j]);
}
struct edge
{
    note arr[7000000];
    int  st[MAXM];
    ll   dis[MAXM];
    ll   h[MAXM];
    int  cur[MAXM];
    int  depth[MAXM];
    int  pre[MAXM];
    int  pree[MAXM];
    int  top;
    int n, m, s, t, siz, k;
    edge()
    {
        memset(st, -1, sizeof(st));
        memset(depth, -1, sizeof(depth));
        memset(dis, -1, sizeof(dis));
        top = 0;
    }
    void init()
    {
        memset(st, -1, sizeof(st));
        memset(depth, -1, sizeof(depth));
        memset(dis, -1, sizeof(dis));
        top = 0;

    }
    void build(int ts, int tt)
    {
       
        n = tn; m = tm; s = ts, t = sum*m+n+1, siz = sum*m+n+1;
        for (int i = 1; i <= n; i++)
            add(s, i+sum*m, p[i], 0);
        s = 0, t = sum * m + n + 1;
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                add(i + sum * m, (j - 1) * sum + 1, 1, a[i][j]);
            }
        }
        for (int i = 1; i <= m; i++)
            add((i - 1) * sum + 1, t, 1, 0);
        for (int i = 1; i <= m; i++)
        {
            for (int j = 1; j <= sum; j++)
            {
                int tmp = (i - 1) * sum + j;
                ha[tmp] = j; co[tmp] = i;
            }

        }
    }
    bool dep()
    {
        queue<int> q;
        q.push(s);
        memset(depth, -1, sizeof(depth));
        depth[s] = 0;
        while (!q.empty())
        {
            int v = q.front(); q.pop();
            for (int i = st[v]; i != -1; i = arr[i].nt)
            {
                int to = arr[i].to;
                if (!arr[i].cal)
                    continue;
                if (depth[to] != -1)
                    continue;
                depth[to] = depth[v] + 1;
                q.push(to);
            }
        }
        return (depth[t] != -1);
    }
    void add(int x, int y, ll z, ll c)
    {
        top++; arr[top] = { y,st[x],top + 1,c,z }; st[x] = top;
        top++; arr[top] = { x,st[y],top - 1,-c,0 }; st[y] = top;
    }
    ll dfs(int now, ll val)
    {
        if (now == t || !val)
            return val;
        ll flow = 0;
        for (int& i = cur[now]; i != -1; i = arr[i].nt)
        {
            int to = arr[i].to;
            if (depth[to] != depth[now] + 1)
                continue;
            ll f = dfs(to, min(arr[i].cal, val));
            if (!f || !arr[i].cal)
                continue;
            flow += f;
            arr[i].cal -= f;
            arr[arr[i].rev].cal += f;
            val -= f;
            if (!val)
                return flow;
        }
        return flow;
    }
    ll dinic()
    {
        ll flow = 0;
        ll f;
        while (dep())
        {
            for (int i = 0; i <= siz; i++)
                //这里
                cur[i] = st[i];
            while (f = dfs(s, INF))
                flow += f;
        }
        return flow;
    }
    ll min_cost(ll f)
    {
        ll res = 0;

        fill(dis, dis + 1 + siz, INF);
        fill(h, h+1+siz, 0);
        priority_queue<P, vector<P>, greater<P>> q;
        dis[s] = 0;
        q.push(P(0, s));
        while (!q.empty())
        {
            P p = q.top(); q.pop();
            int v = p.second;
            if (dis[v] < p.first) continue;
            for (int i = st[v]; i != -1; i = arr[i].nt)
            {
                note& e = arr[i];
                if (e.cal > 0 && dis[e.to] > dis[v] + e.cost + h[v] - h[e.to])
                {
                    dis[e.to] = dis[v] + e.cost + h[v] - h[e.to];
                    pre[e.to] = v;
                    pree[e.to] = i;
                    q.push(P(dis[e.to], e.to));
                }
            }
        }
        if (dis[t] == INF)    return -1;
        for (int i = 0; i <= siz; i++)
            //这里改了
            h[i] += dis[i];
        ll d = f;
        //printf("2\n");
        for (int i = t; i != s; i = pre[i])
        {
            d = min(d, arr[pree[i]].cal);
            arr[pree[i]].cal -= d;;
            int rev = arr[pree[i]].rev;
            arr[rev].cal += d;
        }
    
        res += d * h[t];
        //printf("3\n");
        return res;
    }
};
edge road, new_road;
edge tmp;
int main()
{
    read();
    road.init();
    road.build(0, tn + sum * tm + 1);
    int ret = 0,ans=0;
    int n = tn, m = tm;
    while(1)
    {
        ret = road.min_cost(1);
        if (ret == -1)
            break;
        ans += ret;
        int pos = road.arr[road.pree[road.t]].rev;
        int tmp = road.arr[pos].to;
        road.add(tmp + 1, road.t, 1, 0);
        for (int i = 1; i <= n; i++)
        {
            road.add(i + m * sum, tmp + 1, 1, a[i][co[tmp]] * (ha[tmp] + 1));
        }

    }
    printf("%d", ans);
    return 0;
}
上一篇:$NOI2012$迷失游乐园


下一篇:P2050 [NOI2012]美食节