P2050 [NOI2012]美食节

首先所有菜要被做完,那对于每种菜分配它个数个流量

发现一个同学需要等待的时间仅和他选择的厨师第几个做他的菜有关,考虑将厨师第几次做菜拆成点

如果厨师是倒数第 \(k\) 个做,则有 \(k a[i][j]\) 个代价被等待后 \(k\) 个菜的同学贡献

本身费用流这种算法就有浓重的贪心(最短路)气息,我们可以根据它贪心(最短路)的特性,进行一些边数上的优化

考虑一个厨师,如果倒数第一道菜没被做的话,一定不会做倒数第二道

因为第一道菜的边权最小,从最短路的角度考虑一定会选它

每次都在最后一道菜被选后再连其他的边,就可以优化了

//#include <bits/stdc++.h>
#include <queue>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstring>
#pragma GCC optimize(2)
using namespace std;
#define rg register
//#ifdef ONLINE_JUDGE
//char ss[1 << 17], *A = ss, *B = ss;
//inline char getchar(){ if(A == B){ B = (A = ss) + fread(ss, 1, 1 << 17, stdin); if(A == B) return EOF; } return *A++; }
//#endif
inline int read(){
    rg char ch = getchar();
    rg int x = 0, f = 0;
    while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
    return f ? -x : x;
}
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define req(i, a ,b) for(int i = a; i >= b; --i)
const int N = 100 * 800 + 45, M = N * 20; 
int head[N], ver[M], nxt[M], flow[M], edge[M], tot = 1;
inline void add(int x, int y, int f, int z){
    ver[++tot] = y;
    nxt[tot] = head[x];
    head[x] = tot;
    edge[tot] = z;
    flow[tot] = f;
}
inline void adds(int x, int y, int f, int z){
    add(x, y, f, z);
    add(y, x, 0, -z);
}
int maxpoint;
int n, m, S, T;
int suma[55], p[105][105];
#define inf 0x3f3f3f3f
int v[N], dis[N], pre[N];
template <typename T> inline void ckmax(T &a, const T &b){ if(a < b) a = b; }
inline int spfa(){
	queue<int> q;
	q.push(S);
    rep(i, 1, maxpoint) dis[i] = inf;
    dis[T] = inf;
    dis[S] = 0;
    while(!q.empty()){
        int x = q.front(); q.pop();
        v[x] = false;
        for(int i = head[x]; i; i = nxt[i]){
            int y = ver[i];
            if(dis[y] > dis[x] + edge[i] && flow[i]){
                dis[y] = dis[x] + edge[i];
                pre[y] = i;
                if(!v[y]){
                    q.push(y);
					v[y] = true;
//                    if(!q.empty() && dis[y] < dis[q.front()]) q.push_front(y);
//                    else q.push_back(y);
                }
            }
        }
    }
    return dis[T] != inf;
}
template <typename T> inline void ckmin(T &a, const T &b){ if(a > b) a = b; }
int cnt[N];
inline int MCMF(){
    int ans = 0;
    rep(i, 1, m) cnt[i] = 1;
    rep(i, 1, n) rep(j, 1, m) adds(i, n + j, 1, p[i][j]);
    while(spfa()){
        for(int i = pre[T]; i; i = pre[ver[i ^ 1]]) flow[i] -= 1, flow[i ^ 1] += 1;
        ans += dis[T];
		int to = (ver[pre[T] ^ 1] - n - 1) % m + 1;
        ++cnt[to];
        ckmax(maxpoint, n + (cnt[to] - 1) * m + to);
		rep(i, 1, n) adds(i, n + (cnt[to] - 1) * m + to, 1, cnt[to] * p[i][to]); 
		adds(n + (cnt[to] - 1) * m + to, T, 1, 0);
    }
    return ans;
}
signed main(){
    n = read(), m = read();
    int sum = 0;
    rep(i, 1, n) sum += (suma[i] = read());
    S = 0; T = n + sum * m + 1;
    rep(i, 1, n) rep(j, 1, m) p[i][j] = read();
    rep(i, 1, n) adds(S, i, suma[i], 0);
    rep(i, 1, m) adds(n + i, T, 1, 0);
    maxpoint = n + m;
    cout<<MCMF()<<endl;
    getchar(); getchar();
    return 0;
}
posted @ 2020-06-12 16:09  __int256  阅读(132)  评论(0编辑  收藏  举报