[BZOJ2879][Noi2012]美食节 && 动态加边费用流

12 篇文章 0 订阅
11 篇文章 0 订阅

看上去和修车一模一样的东西  可是数据范围大太多铁定要T

然后听说了动态加边这个东西感觉虽然不能改变时间复杂度 但是还是很有用的样子

每次增广后找到该次的厨师给它新增一个点 然后费用逐步递增 (最后一个菜贡献1倍 倒数第二个贡献两倍)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#include<deque>
#define SF scanf
#define PF printf
using namespace std;
typedef long long LL;
const int MAXN = 100000;
const int INF = 0x3f3f3f3f;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); }
    while(ch >= '0' && ch <= '9') { x = 10*x + ch-'0'; ch = getchar(); }
    return x*f;
}
int c[MAXN+10], tot, t[50][110], now[MAXN+10];
int n, m, S, T, ans, ecnt = 2, ncnt;
queue <int> q;
struct MCMF {
    int N, M;
    int d[MAXN+10], p[MAXN+10], a[MAXN+10], inq[MAXN+10];
    struct Node {
        int u, v, c, cost, next;
    } Edge[MAXN*60+10];
    int adj[MAXN+10];
    void addedge(int u, int v, int c, int wt) {
        Node &e = Edge[ecnt];
        e.u = u; e.v = v; e.c = c; e.cost = wt;
        e.next = adj[u]; adj[u] = ecnt++;
    }
    void add(int u, int v, int c, int cost) {
        addedge(u, v, c, cost); addedge(v, u, 0, -cost);
    }
    bool SPFA(int s, int t, int &Flow, int &Cost) {
        memset(d, 0x3f, sizeof(d));
        d[s] = 0; a[s] = INF; p[s] = 0; inq[s] = 1;
        q.push(s);
        while(!q.empty()) {
            int u = q.front(); q.pop();
            inq[u] = 0;
            for(int i = adj[u]; i; i = Edge[i].next) {
                Node &e = Edge[i];
                int v = e.v, c = e.c, wt = e.cost;
                if(c && d[v] > d[u] + wt) {
                    d[v] = d[u] + wt;
                    a[v] = min(a[u], c);
                    p[v] = i;
                    if(!inq[v]) {
                        inq[v] = true;
                        q.push(v);
                    }
                }
            }
        }
        if(d[t] == INF) return false;
        return true;
    }
    void extend() {
        int A, B;
        for(int i = T; i ^ S; i = Edge[p[i]].u) {
            Edge[p[i]].c -= a[T];
            Edge[p[i]^1].c += a[T];
            if(i <= m && i >= 1) A = i;
            if(i != T && i > m) B = i;
        }
        ++now[A];
        add(A, ++ncnt, 1, 0);
        for(int i = 1; i <= n; i++)
            add(ncnt, m+i, 1, (now[A]+1) * t[i][A]);
        ans += a[T] * d[T];
    }
    int Mincost(int s, int t) {
        int Flow = 0, Cost = 0;
        while(SPFA(s, t, Flow, Cost)) 
            extend();
        return Cost;
    }
} sap;
int main() {
    S = 0; T = MAXN+1;
    n = read(); m = read();
    for(int i = 1; i <= n; i++) tot += (c[i] = read());
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            t[i][j] = read();
    for(int i = 1; i <= m; i++) sap.add(S, ++ncnt, INF, 0);
    for(int i = 1; i <= n; i++) sap.add(++ncnt, T, c[i], 0);
    for(int i = 1; i <= m; i++) {
        sap.add(i, ++ncnt, 1, 0);
        for(int j = 1; j <= n; j++)
            sap.add(ncnt, m+j, 1, t[j][i]);
    }
    sap.Mincost(S, T);
    PF("%d", ans);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值