[BZOJ2879][Noi2012]美食节(费用流)
题目描述
CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节。作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴。他很快就尝遍了美食节所有的美食。然而,尝鲜的欲望是难以满足的。尽管所有的菜品都很可口,厨师做菜的速度也很快,小M仍然觉得自己桌上没有已经摆在别人餐桌上的美食是一件无法忍受的事情。于是小M开始研究起了做菜顺序的问题,即安排一个做菜的顺序使得同学们的等待时间最短。小M发现,美食节共有n种不同的菜品。每次点餐,每个同学可以选择其中的一个菜品。总共有m个厨师来制作这些菜品。当所有的同学点餐结束后,菜品的制作任务就会分配给每个厨师。然后每个厨师就会同时开始做菜。厨师们会按照要求的顺序进行制作,并且每次只能制作一人份。此外,小M还发现了另一件有意思的事情: 虽然这m个厨师都会制作全部的n种菜品,但对于同一菜品,不同厨师的制作时间未必相同。他将菜品用1, 2, ..., n依次编号,厨师用1, 2, ..., m依次编号,将第j个厨师制作第i种菜品的时间记为 ti,j 。小M认为:每个同学的等待时间为所有厨师开始做菜起,到自己那份菜品完成为止的时间总长度。换句话说,如果一个同学点的菜是某个厨师做的第k道菜,则他的等待时间就是这个厨师制作前k道菜的时间之和。而总等待时间为所有同学的等待时间之和。现在,小M找到了所有同学的点菜信息: 有 pi 个同学点了第i种菜品(i=1, 2, ..., n)。他想知道的是最小的总等待时间是多少。
输入输出格式
输入格式:
输入文件的第1行包含两个正整数n和m,表示菜品的种数和厨师的数量。 第2行包含n个正整数,其中第i个数为pi,表示点第i种菜品的人数。 接下来有n行,每行包含m个非负整数,这n行中的第i行的第j个数为ti,j,表示第j个厨师制作第i种菜品所需的时间。 输入文件中每行相邻的两个数之间均由一个空格隔开,行末均没有多余空格。
输出格式:
输出仅一行包含一个整数,为总等待时间的最小值。
输入输出样例
说明
厨师1先制作1份菜品2,再制作2份菜品1。点这3道菜的3个同学的等待时间分别为3,3+5=8,3+5+5=13。
厨师2先制作1份菜品1,再制作1份菜品3。点这2道菜的2个同学的等待时间分别为7,7+9=16。
总等待时间为3+8+13+7+16=47。
虽然菜品1和菜品3由厨师1制作更快,如果这些菜品都由厨师1制作,总等待时间反而更长。如果按上述的做法,将1份菜品1和1份菜品3调整到厨师2制作,这样厨师2不会闲着,总等待时间更短。
可以证明,没有更优的点餐方案。 每组数据的n、m和p值如下:
| 测试点编号 | n | m | p |
|---|---|---|---|
| 1 | n = 5 | m = 5 | p = 10 |
| 2 | n = 40 | m = 1 | p = 400 |
| 3 | n = 40 | m = 2 | p = 300 |
| 4 | n = 40 | m = 40 | p = 40 |
| 5 | n = 5 | m = 40 | p = 100 |
| 6 | n = 10 | m = 50 | p = 200 |
| 7 | n = 20 | m = 60 | p = 400 |
| 8 | n = 40 | m = 80 | p = 600 |
| 9 | n = 40 | m = 100 | p = 800 |
| 10 | n = 40 | m = 100 | p = 800 |
对于100%的数据,n <= 40, m <= 100, p <= 800, ti,j <= 1000 (其中p = ∑pi)
题解
做这题之前建议先去做bzoj1070【SCOI2007】修车
基本上一模一样,除了数据范围……直接加边绝对爆炸……
然后没办法只能动态加边了……
考虑一下,它每一次跑spfa最多只会增广出一条路径,也就是说只会求出一个人修的倒数第$k$辆车一个厨师做的倒数第$k$道菜
那么只要在每一次spfa之后,把它之后需要跑的边再动态建上去就行了
然后有一些细节问题,写在代码里了
//minamoto
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x3f3f3f3f
#define id(i,j) ((i-1)*p+j+n)//表示(i,j)对应的编号
#define l(x) ((x-1-n)/p+1)//表示编号对应的i
#define r(x) ((x-1-n)%p+1)//表示编号对应的j
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[<<],*p1=buf,*p2=buf;
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while(!isdigit(ch=getc()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getc());res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
const int N=,M=;
int ver[M],Next[M],head[N],edge[M],flow[M],tot=;
int dis[N],disf[N],Pre[N],last[N],vis[N];
int n,m,s,t,ans,p,cost[][];
queue<int> q;
inline void add(int u,int v,int f,int e){
ver[++tot]=v,Next[tot]=head[u],head[u]=tot,flow[tot]=f,edge[tot]=e;
ver[++tot]=u,Next[tot]=head[v],head[v]=tot,flow[tot]=,edge[tot]=-e;
}
bool spfa(){
memset(dis,0x3f,sizeof(dis));
q.push(s),dis[s]=,disf[s]=inf,Pre[t]=-;
while(!q.empty()){
int u=q.front();q.pop();vis[u]=;
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(flow[i]&&dis[v]>dis[u]+edge[i]){
dis[v]=dis[u]+edge[i],Pre[v]=u,last[v]=i;
disf[v]=min(disf[u],flow[i]);
if(!vis[v]) vis[v]=,q.push(v);
}
}
}
return ~Pre[t];
}
void dinic(){
while(spfa()){
int u=t;ans+=disf[t]*dis[t];
while(u!=s){
flow[last[u]]-=disf[t],flow[last[u]^]+=disf[t];
u=Pre[u];
}
u=ver[last[t]^];
add(u+,t,,);
//下一个点就是u+1
//因为点的编号是((i-1)*p+j+n)
//i与菜有关
//j表示做倒数第几道菜
//然后现在的选了,要选之后的
for(int i=;i<=n;++i)
add(i,u+,,cost[i][l(u+)]*r(u+));
}
}
int main(){
n=read(),m=read();
s=;
for(int i=;i<=n;++i){
int t1=read();p+=t1;
add(s,i,t1,);
}
t=n+p*m+;
for(int i=;i<=n;++i)
for(int j=;j<=m;++j){
cost[i][j]=read();
add(i,id(j,),,cost[i][j]);
}
for(int j=;j<=m;++j) add(id(j,),t,,);
dinic();
printf("%d\n",ans);
return ;
}
[BZOJ2879][Noi2012]美食节(费用流)的更多相关文章
- [BZOJ2879][NOI2012]美食节(费用流)
设sm为所有p之和,套路地对每道菜建一个点,将每个厨师拆成sm个点,做的倒数第i道菜的代价为time*i. S向每道菜连边<0,p[i]>(前者为代价后者为流量),i菜到j厨师的第k个点连 ...
- [BZOJ2879] [Noi2012] 美食节 (费用流 & 动态加边)
Description CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食节所有的美食.然而,尝鲜的欲望是难以满足的.尽 ...
- 【bzoj2879】[Noi2012]美食节 费用流+动态加边
原文地址:http://www.cnblogs.com/GXZlegend 题目描述 CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他 ...
- [NOI2012]美食节(费用流)
题目描述 CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食节所有的美食.然而,尝鲜的欲望是难以满足的.尽管所有的菜品都 ...
- [NOI2012]美食节——费用流(带权二分图匹配)+动态加边
题目描述 小M发现,美食节共有n种不同的菜品.每次点餐,每个同学可以选择其中的一个菜品.总共有m个厨师来制作这些菜品.当所有的同学点餐结束后,菜品的制作任务就会分配给每个厨师.然后每个厨师就会同时开始 ...
- BZOJ 2879: [Noi2012]美食节( 费用流 + 动态加边 )
倒着做菜..然后考虑为当前的人做菜对后面的人的影响就可以了..要动态加边 --------------------------------------------------------------- ...
- BZOJ.2879.[NOI2012]美食节(费用流SPFA)
题目链接 /* 同"修车":对于每个厨师拆成p个点表示p个时间点,每个人向m个厨师每个时间点连边 这样边数O(nmp)+网络流 ≈O(nm*p^2)(假设SPFA线性) = GG ...
- 【BZOJ 2879】[Noi2012]美食节 费用流
思路同修车,就是多了一个骚气的操作:动态加边,我们通过spfa流的过程可以知道,我们一次只会跑一流量,最后一层边跑过就不会再悔改,所以说我们只会用到一大片里面的很少的点,所以我们如果可以动态加边的话我 ...
- BZOJ 2879 [Noi2012]美食节 | 费用流 动态开点
这道题就是"修车"的数据加强版--但是数据范围扩大了好多,应对方法是"动态开点". 首先先把"所有厨师做的倒数第一道菜"和所有菜连边,然后跑 ...
- [NOI2012][bzoj2879] 美食节 [费用流+动态加边]
题面 传送门 思路 先看看这道题 修车 仔细理解一下,这两道题是不是一样的? 这道题的不同之处 但是有一个区别:本题中每一种车有多个需求,但是这个好办,连边的时候容量涨成$p\lbrack i\rbr ...
随机推荐
- java成神之——线程操作
线程 Future CountDownLatch Multithreading synchronized Thread Producer-Consumer 获取线程状态 线程池 ThreadLocal ...
- Tuple、list的区别以及dict和set
元组(Tuple): 定义方法:使用小括号() 使用方法: count:可以统计某个元组段在整个元组中出现的次数 index:可以查询某个元组段在整个元组中的元组号 name_tuple = ('xi ...
- PHP PDO SQLSERVER
$bbs = new PDO("odbc:MSSQLServer", $username_bbs, $password_bbs $bbs = new PDO('); $s ...
- ref & out - C#中的参数传递
[ref & out - C#中的参数传递] ref与out均指定函数参数按引用传递,惟一的不同是,ref传递的参数必须初始化,而out可以不用. ref与out无法作为重载的依据,即ref与 ...
- CountDownLatch、信号量
countDownlatch可以阻塞线程,可以在某种条件下继续执行 不安全的:
- pipeline 对部分特征进行处理
http://scikit-learn.org/stable/auto_examples/preprocessing/plot_function_transformer.html#sphx-glr-a ...
- Django JSON-RPC
Django JSON-RPC https://github.com/samuraisam/django-json-rpc =============== A basic JSON-RPC Imple ...
- js 禁止后退键
function doKey(e) { var ev = e || window.event; //获取event对象 var obj = ev.target || ev.srcElement; // ...
- Entity Framework 6.0 Tutorials(9):Stored Procedure Mapping
Code First - Insert, Update, Delete Stored Procedure Mapping: Entity Framework 6 Code-First provides ...
- IDEA小技巧:添加代码快捷方式
非常怀恋eclipse的的代码快捷方式tryc,今天给IDEA也添加了一个