洛谷P2050 [NOI2012]美食節
阿新 • • 發佈:2018-12-14
如果沒有做過修車(本題的弱化版),可以先去做一下。
大致思路就是把每個廚師拆成\(p\)個點,分別代表倒數第幾個做哪道菜,然後從每種菜向廚師連邊,倒數第幾個的邊權就是幾倍的等待時間,最後跑一波費用流即可。
但本題的資料範圍有些殘酷,所以我們考慮動態加邊。最多隻會跑\(p\)次\(spfa\),並且每次走最短路增廣,所以很多邊都是沒有用的。首先把代表倒數第一個做的菜的邊加上。然後,比如某一次跑完\(spfa\)後,是沿第\(j\)個廚師的倒數第\(k\)個增廣的,那就把他的代表倒數\(k+1\)的邊補全。
程式碼如下:
#include <bits/stdc++.h> using namespace std; #define N 40 #define M 100 #define P 800 #define SZ (N+P*M) #define pb push_back #define INF 0x3f3f3f3f struct Edge { int from, to, cap, cost; }; int n, m, tot, S, T, ans, ans0, t[N+5], d[SZ+5], vis[SZ+5], a[SZ+5], pre[SZ+5], tim[M+5][N+5], lim[M+5]; vector<int> G[SZ+5]; vector<Edge> edges; int ID(int j, int k) { return n+(j-1)*tot+k; } int rev(int id) { return (id-n-1)/tot+1; } void addEdge(int u, int v, int cap, int cost) { edges.pb(Edge{u, v, cap, cost}), edges.pb(Edge{v, u, 0, -cost}); G[u].pb(edges.size()-2), G[v].pb(edges.size()-1); } int spfa() { memset(d, 0x3f, sizeof d); d[S] = 0, vis[S] = 1, a[S] = INF, pre[S] = 0; queue<int> q; q.push(S); while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for(int i = 0, v, w; i < G[u].size(); ++i) { Edge &e = edges[G[u][i]]; v = e.to, w = e.cost; if(e.cap > 0 && d[v] > d[u]+w) { d[v] = d[u]+w; a[v] = min(a[u], e.cap); pre[v] = G[u][i]; if(!vis[v]) q.push(v), vis[v] = 1; } } } if(d[T] == INF) return 0; int u = T; ans0 += a[T], ans += d[T]*a[T]; while(u != S) { edges[pre[u]].cap -= a[T], edges[pre[u]^1].cap += a[T]; u = edges[pre[u]].from; } return 1; } int main() { #ifndef ONLINE_JUDGE freopen("testdata.in", "r", stdin); freopen("testdata.out", "w", stdout); #endif scanf("%d%d", &n, &m); S = 0; for(int i = 1; i <= n; ++i) scanf("%d", &t[i]), tot += t[i], addEdge(S, i, t[i], 0); T = n+tot*m+1; for(int i = 1; i <= n; ++i) { for(int j = 1, t; j <= m; ++j) scanf("%d", &tim[j][i]), addEdge(i, ID(j, 1), 1, tim[j][i]), lim[j] = 1; } for(int i = n+1; i <= n+tot*m; ++i) addEdge(i, T, 1, 0); while(spfa()) { int j = rev(edges[pre[T]].from); lim[j]++; for(int i = 1; i <= n; ++i) addEdge(i, ID(j, lim[j]), 1, lim[j]*tim[j][i]); } printf("%d\n", ans); return 0; }