ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2020-03-15 17:56:23  阅读:286  来源: 互联网

标签:arr int luogu ll depth NOI2012 top 美食节 dis


【题目大意】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;
}

标签:arr,int,luogu,ll,depth,NOI2012,top,美食节,dis
来源: https://www.cnblogs.com/rentu/p/12498707.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有