@loj - 2674@ 「NOI2012」美食节
@description@
CZ 市为了欢迎全国各地的同学,特地举办了一场盛大的美食节。
作为一个喜欢尝鲜的美食客,小 M 自然不愿意错过这场盛宴。他很快就尝遍了美食节所有的美食。然而,尝鲜的欲望是难以满足的。尽管所有的菜品都很可口,厨师做菜的速度也很快,小 M 仍然觉得自己桌上没有已经摆在别人餐桌上的美食是一件无法忍受的事情。于是小 M 开始研究起了做菜顺序的问题,即安排一个做菜的顺序使得同学们的等待时间最短。
小 M 发现,美食节共有 n 种不同的菜品。每次点餐,每个同学可以选择其中的一个菜品。总共有 m 个厨师来制作这些菜品。当所有的同学点餐结束后,菜品的制作任务就会分配给每个厨师。然后每个厨师就会同时开始做菜。厨师们会按照要求的顺序进行制作,并且每次只能制作一人份。
此外,小 M 还发现了另一件有意思的事情——虽然这 m 个厨师都会制作全部的 n 种菜品,但对于同一菜品,不同厨师的制作时间未必相同。他将菜品用 1, 2, ..., n 依次编号,厨师用 1, 2, ..., m 依次编号,将第 j 个厨师制作第 i 种菜品的时间记为 tij。
小 M 认为:每个同学的等待时间为所有厨师开始做菜起,到自己那份菜品完成为止的时间总长度。换句话说,如果一个同学点的菜是某个厨师做的第 k 道菜,则他的等待时间就是这个厨师制作前 k 道菜的时间之和。而总等待时间为所有同学的等待时间之和。
现在,小 M 找到了所有同学的点菜信息——有 pi 个同学点了第 i 种菜品(i = 1, 2, ..., n)。他想知道的是最小的总等待时间是多少。
输入格式
输入文件的第 1 行包含两个正整数 n 和 m,表示菜品的种数和厨师的数量。
第 2 行包含 n 个正整数,其中第 i 个数为 pi,表示点第 i 种菜品的人数。
接下来有 n 行,每行包含 m 个非负整数,这 n 行中的第 i 行的第 j 个数为 tij,表示第 j 个厨师制作第 i 种菜品所需的时间。
输入中每行相邻的两个数之间均由一个空格隔开,行末均没有多余空格。
输出格式
输出仅一行包含一个整数,为总等待时间的最小值。
样例
样例输入
3 2
3 1 1
5 7
3 6
8 9
样例输出
47
数据范围与提示
n <= 40, m <= 100, ∑p <= 800, tij <= 1000。
@solution@
每个厨师会选择一些菜品制作,可以看作厨师与菜品的匹配,联想到网络流。
把菜品看作流量,等待时间看作费用。如果可以通过某种建图使得第 i 个菜品对应的流量 = 容量 = pi,在此基础上费用最少,就可以直接跑最小费用最大流求解。
假如一个厨师先后做了等待时间为 t1, t2, ..., tk 的菜品,则这个厨师对应的总等待时间为 t1 + (t1 + t2) + ... , (t1 + t2 + ... + tk) = k*t1 + (k-1)*t2 + ... + 1*tk。
看起来很有规律,但是这个代价与 k 有关,当 k 是个不确定的量时不好求解。我们不妨换种定义,假如一个厨师按照从后到前的顺序依次做了等待时间为 t1', t2', ..., tk',那么这个厨师对应的总等待时间为 t1'*1 + t2'*2 + ... + tk'*k。这样每一项就与 k 无关了。
但是始终还有一个系数,无法直接搬到费用流上面去。不妨考虑大胆拆点,将第 j 个厨师拆成 ∑p 个点,第 (j, i) 个点表示第 j 个厨师做倒数第 i 道菜品。这样系数的问题就解决了。
然后源点 s 向 m 个厨师的 ∑p 个点连容量为 1,费用为 0 的边;所有的 n 种菜品向汇点 t 连容量为 pi,费用为 0 的边;第 j 个厨师的第 k 个点向第 i 中菜品连容量为 1,费用为 k * tij 的边。
跑最小费用最大流即可。
看起来非常完美。但实际上,即使网络流的玄学复杂度也跑不过这道题的数据。
解决方法是,注意到增广时总沿着最短路增广,而第 j 个厨师的第 k 个点连出去的最短路总是比第 j 个厨师的第 k+1 个点连出去的最短路要更短。
于是我们当且仅当第 k 个点增广过后,才把第 k+1 个点的相关边加入流网络中。
这样为什么会快很多?很简单嘛。你虽然拆出来 m*∑p 个点,但实际上有用的也不过 ∑p 个点,所以我们每一次增广的图的点数就从理论的 m*∑p 坍缩成 ∑p。而 m*∑p 与 ∑p 根本也不是一个量级的嘛。
@accepted code@
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 40;
const int MAXM = 100;
const int MAXP = 800;
const int MAXV = MAXP*MAXM + MAXN + 5;
const int MAXE = 20*MAXV + 5;
const int INF = (1<<30);
struct FlowGraph{
struct edge{
int to, cap, flow, cost;
edge *nxt, *rev;
}edges[MAXE + 5], *adj[MAXV + 5], *cur[MAXV + 5], *ecnt;
FlowGraph() {ecnt = &edges[0];}
int s, t, n;
void addedge(int u, int v, int c, int w) {
edge *p = (++ecnt), *q = (++ecnt);
p->to = v, p->cap = c, p->flow = 0, p->cost = w;
p->nxt = adj[u], adj[u] = p;
q->to = u, q->cap = 0, q->flow = 0, q->cost = -w;
q->nxt = adj[v], adj[v] = q;
p->rev = q, q->rev = p;
}
int hp[MAXV + 5], f[MAXV + 5];
void update(int x, int k) {
f[x] = k;
while( x ) {
hp[x] = x;
if( (x<<1) <= n && f[hp[x]] > f[hp[x<<1]] )
hp[x] = hp[x<<1];
if( (x<<1|1) <= n && f[hp[x]] > f[hp[x<<1|1]] )
hp[x] = hp[x<<1|1];
x >>= 1;
}
}
int d[MAXV + 5], h[MAXV + 5];
bool relabel() {
for(int i=1;i<=n;i++)
h[i] += d[i], d[i] = f[i] = INF, hp[i] = i, cur[i] = adj[i];
update(s, d[s] = 0);
while( f[hp[1]] != INF ) {
int x = hp[1]; update(x, INF);
for(edge *p=adj[x];p;p=p->nxt) {
int w = p->cost + h[x] - h[p->to];
if( p->cap > p->flow && d[x] + w < d[p->to] )
update(p->to, d[p->to] = d[x] + w);
}
}
return !(d[t] == INF);
}
bool vis[MAXV + 5];
int aug(int x, int tot) {
if( x == t ) return tot;
int sum = 0; vis[x] = true;
for(edge *&p=cur[x];p;p=p->nxt) {
int w = p->cost + h[x] - h[p->to];
if( p->cap > p->flow && !vis[p->to] && d[x] + w == d[p->to] ) {
int del = aug(p->to, min(tot - sum, p->cap - p->flow));
p->flow += del, p->rev->flow -= del, sum += del;
if( sum == tot ) break;
}
}
vis[x] = false;
return sum;
}
}G;
int T[MAXN + 5][MAXM + 5], p[MAXN + 5], n, m;
FlowGraph::edge *e[MAXM + 5]; int id[MAXM + 5], cnt[MAXM + 5];
int main() {
scanf("%d%d", &n, &m);
G.s = n + 1, G.t = G.n = n + 2;
for(int i=1;i<=n;i++) {
scanf("%d", &p[i]);
G.addedge(G.s, i, p[i], 0);
}
for(int j=1;j<=m;j++)
id[j] = (++G.n), G.addedge(id[j], G.t, 1, 0), e[j] = G.ecnt, cnt[j] = 1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) {
scanf("%d", &T[i][j]);
G.addedge(i, id[j], 1, cnt[j]*T[i][j]);
}
int ans = 0;
while( G.relabel() ) {
int del = G.aug(G.s, INF);
ans += del*(G.d[G.t] + G.h[G.t]);
for(int j=1;j<=m;j++)
if( e[j]->flow ) {
id[j] = (++G.n), G.addedge(id[j], G.t, 1, 0), e[j] = G.ecnt, cnt[j]++;
for(int i=1;i<=n;i++)
G.addedge(i, id[j], 1, cnt[j]*T[i][j]);
}
}
printf("%d\n", ans);
}
@details@
做完这道题你就可以继续去做SCOI的修车了。
@loj - 2674@ 「NOI2012」美食节的更多相关文章
- 【LOJ】#2674. 「NOI2012」美食节
题解 这道题的费用流如果朴素一点怎么建边呢 建出\(\sum_{i = 1}^{n} p^{i} M\)个点,第\(i\)个厨师的第\(j\)个点表示这个厨师倒数第\(j\)个做的是某道菜 这个点向汇 ...
- Loj #2192. 「SHOI2014」概率充电器
Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完 ...
- Loj #3096. 「SNOI2019」数论
Loj #3096. 「SNOI2019」数论 题目描述 给出正整数 \(P, Q, T\),大小为 \(n\) 的整数集 \(A\) 和大小为 \(m\) 的整数集 \(B\),请你求出: \[ \ ...
- Loj #3093. 「BJOI2019」光线
Loj #3093. 「BJOI2019」光线 题目描述 当一束光打到一层玻璃上时,有一定比例的光会穿过这层玻璃,一定比例的光会被反射回去,剩下的光被玻璃吸收. 设对于任意 \(x\),有 \(x\t ...
- Loj #3089. 「BJOI2019」奥术神杖
Loj #3089. 「BJOI2019」奥术神杖 题目描述 Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的 ...
- Loj #2542. 「PKUWC2018」随机游走
Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次 ...
- Loj #3059. 「HNOI2019」序列
Loj #3059. 「HNOI2019」序列 给定一个长度为 \(n\) 的序列 \(A_1, \ldots , A_n\),以及 \(m\) 个操作,每个操作将一个 \(A_i\) 修改为 \(k ...
- Loj #3056. 「HNOI2019」多边形
Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...
- Loj #3055. 「HNOI2019」JOJO
Loj #3055. 「HNOI2019」JOJO JOJO 的奇幻冒险是一部非常火的漫画.漫画中的男主角经常喜欢连续喊很多的「欧拉」或者「木大」. 为了防止字太多挡住漫画内容,现在打算在新的漫画中用 ...
随机推荐
- jQuery控制导航条样式
原理:点击当前元素时,当前元素添加(样式类),父辈的兄弟姐妹的孩子('a')去掉此样式类. 代码如下: /*次要导航*/ $(".subnav li a").click(funct ...
- python 常见分布的产生方式
- jquery Select2 学习笔记之中文提示 - CSDN博客
首先学习这个东西呢,还是看官网比较全面 select2官网例子 要select2中文显示:必须要引入中文包,且一定要放在select2.js之后 [javascript] view plain cop ...
- spring和mybatis整合遇到org.springframework.beans.factory.BeanDefinitionStoreException
今天对spring和mybatis整合进行练习,通过MapperScannerConfigurer进行mapper扫描 但是在进行单元测试的时候,死活就是报错,具体报错如下: org.springfr ...
- QT_强杀进程
#ifdef WIN32 bool res = false; HANDLE hToolHelp32Snapshot; hToolHelp32Snapshot = CreateToolhelp32Sna ...
- Node.js的框架-express
Node.js的框架 express 是第三方的 express const express=require('express'); const app=express(); const PORT=3 ...
- ArcGISTiledMapServiceLayer
<!DOCTYPE html><html><head> <meta charset="utf-8"> <title>第一 ...
- homeworkvue
两个半圆,点一下转90°,两个颜色 <!DOCTYPE html> <html lang="en"> <head> <meta chars ...
- 【水滴石穿】douban-movies-react-native
这个项目的话,倒是可以做一个支架页面,就是你需要什么东西,你就可以在里面加,不过也是比较难的地方 就是数据流,数据处理的部分.react可以处理数据的方式很多,没有见过类似于古老的vue时候可以使用的 ...
- 用JS实线放大镜的效果
今天花了点时间,复习了下使用原生JS实线放大镜的效果.在制作过程中,也是很到了一些问题,在这里总结下. HTML代码如下: <div id="preview"> < ...