Description
链接扔上来~
Solution
我就觉得是网络流!(我就是不会捉!
这道题的弱化版好像别的题解都写了,什么[SDOI2007]修车吧,然后去看了一下,这道题目就在它的基础上在搞了个动态加边。
首先来建图:
1.把每种菜建个点,从
s
s
连到它的费用为,流量为
pi
p
i
的边。
2.然后给每个厨师的每个炒菜的时刻建个点,注意厨师
a
a
的第一个点叫做厨师倒数第一个做的菜。把这些点跟
t
t
连边,费用为,流量为
1
1
。
3.那么我们把菜向厨师的每个时刻点连边,第个时刻就要把原费用乘
i
i
,这些边流量显然都是。
然后我们就
T
T
飞起了,考虑有好多边不知道加了干嘛根本没有用。具体来说,我们每在中增广一次,就是暂时确定了一个厨师的某个时刻炒了什么菜。显然如果一个厨师的倒数第
i
i
个时刻没被选,那么他的倒数第个时刻显然不会下一次被选。于是我们每增广一次,就找出来那个被确定的厨师,把他的下一个时刻的点添加进去。注意最开始的时候要把所有厨师的倒数第一时刻的点放入。
于是效率为
O(np2∗spfa常数)
O
(
n
p
2
∗
s
p
f
a
常
数
)
。
Source
//2018-5-21
//miaowey
//
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define For(i, a, b) for(int i = (a); i <= (int)(b); ++i)
#define N (100000 + 5)
const int oo = 0x3f3f3f3f;
int n, m, sum, ans, p[N], a[45][105];
struct Edge{
int from, to, cap, flow, cost;
};
namespace MCMF{
bool inq[N];
int s, t, mxf[N], mnc[N], pre[N];
vector<Edge> edges;
vector<int> G[N];
void AddEdge(int u, int v, int w, int co){
edges.pb((Edge){u, v, w, 0, co});
edges.pb((Edge){v, u, 0, 0, -co});
int siz = edges.size();
G[u].pb(siz - 2); G[v].pb(siz - 1);
}
bool Spfa(){
queue<int> q;
For(i, s, t) mnc[i] = oo, mxf[i] = pre[i] = 0;
mnc[s] = 0; mxf[s] = oo; inq[s] = true; q.push(s);
while(!q.empty()){
int now = q.front(); q.pop();
inq[now] = false;
For(i, 0, G[now].size() - 1){
Edge &e = edges[G[now][i]];
if(mnc[e.to] > mnc[now] + e.cost && e.cap > e.flow){
mnc[e.to] = mnc[now] + e.cost;
mxf[e.to] = min(mxf[now], e.cap - e.flow);
pre[e.to] = G[now][i];
if(!inq[e.to]){
inq[e.to] = true; q.push(e.to);
}
}
}
}
if(!mxf[t]) return false;
ans += mxf[t] * mnc[t];
int now = t;
while(now != s){
edges[pre[now]].flow += mxf[t];
edges[pre[now] ^ 1].flow -= mxf[t];
now = edges[pre[now]].from;
}
return true;
}
};
using namespace MCMF;
void Solve(){
while(Spfa()){
int now = edges[pre[t]].from;
AddEdge(now + 1, t, 1, 0);
For(i, 1, n)
AddEdge(i + sum * m, now + 1, 1, a[i][now / sum + 1] * (now % sum + 1));
}
printf("%d\n", ans);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("food.in", "r", stdin);
freopen("food.out", "w", stdout);
#endif
scanf("%d%d", &n, &m);
For(i, 1, n) scanf("%d", &p[i]), sum += p[i];
For(i, 1, n) For(j, 1, m) scanf("%d", &a[i][j]);
s = 0, t = n + sum * m + 1;
For(i, 1, n){
AddEdge(s, i + sum * m, p[i], 0);
For(j, 1, m) AddEdge(i + sum * m, (j - 1) * sum + 1, 1, a[i][j]);
}
For(j, 1, m) AddEdge((j - 1) * sum + 1, t, 1, 0);
Solve();
return 0;
}