题目描述
小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)。他想知道的是最小的总等待时间是多少。
[数据范围]
10 | n = 40 | m = 100 | p = 800 |
其中,$p=\sum_i^n p[i] $
题解
前置:[SCOI2007]修车
修车这个题目,可以把工人拆成n个阶段,m*n个点。
工人j的阶段i的意思是,正在维修,所有排在j这一队的,加上这个汽车后面还有i个汽车的汽车。
即,如果(j,1)表示维修这一队的最后一辆汽车。
汽车放在左部点,工人m*n个点放在右部点。
S向car连接流1费0,工人向T连接流1费0
作用:规定最大流为n。每个工人同一个阶段只能维修一辆。
第i个车向阶段为k的j个工人连接:流1费k*(tr[i][j])
作用:规定这个汽车只能被这个阶段的工人修一次。如果把这个汽车放在后面还有k个情况下修,那么总的等待时间会多出k*tr[i][j]
那么现在,每一个增广路,都代表一个汽车选择了某个工人的某个位置。
并且不会选多,不会重选,不会选少。
直接费用流即可。
但是这个“美食节”就比较麻烦了。
(省选之于国赛。。。)
左部点,总的菜不止n了,但是可以左边n个菜种,S到i的容量变成p[i]即可。
如果还像上面一样暴力建边的话,那么,右部点一共有sum*m个。
然后暴力建立完全二分图
那么,边数会比6e6还多。
亲测会TLE成60分。
怎么优化?
左部点不能少了。为了保证合法,右部点也没法少了。
其实,真正卡SPFA一定是因为边数太多了。
我们真的用得了这么多边么?
显然大部分都是没用的。我们对着上限开了sum*m个点,真正匹配上的也就sum个点罢了。
对于这种问题我们处理地多了。
n个集合,数字一共m个。开n个vector,即动态数组。
需要n棵线段树,一共修改n次。开n棵动态开点线段树。
有什么共同之处??
都叫“动态”
好,那我们就动态加边!!
一共其实只会跑sum次SPFA
而求的是最小费用最大流,也就是求最短路。
所以,第一次一定是某个厨师在1阶段做菜。然后,这个如果需要的话,这个厨师又会在2阶段做菜。
所以,每个厨师很高的阶段,会凭空浪费时间,而且dis太大,根本用不上。
而每次E-K是找到一条增广路增广。
所以,我们开始只要把每个菜种i向阶段1的厨师连边,阶段1的厨师向T连边即可。
然后,SPFA之后的upda,当处理到一个右部点(厨师j和阶段k)时候,
把每个菜种i向这个厨师k+1阶段连一条边。
再把厨师j的k+1阶段向T连接一条边。
(注意一下向T连边,不能单纯只判断是一个右部点,因为网络流是可能回流反悔的。
所以,一条增广路可能多次经过一个右部点。
而第一次经过的右部点才是这条增广路的决策。
立一个flag即可。
)
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=;
const int inf=0x3f3f3f3f;
int n,m;
int p[],sum;
struct node{
int nxt,to,w,c;
}e[*(++)];
int hd[N],cnt=;
int tr[][];
void add(int x,int y,int w,int c){
e[++cnt].nxt=hd[x];
e[cnt].to=y;e[cnt].w=w,e[cnt].c=c;
hd[x]=cnt; e[++cnt].nxt=hd[y];
e[cnt].to=x;e[cnt].w=;e[cnt].c=-c;
hd[y]=cnt;
}
int dis[N],pre[N],incf[N];
bool vis[N];
int s,t;
queue<int>q;
bool spfa(){
memset(dis,0x3f,sizeof dis);
memset(vis,,sizeof vis);
while(!q.empty())q.pop();
dis[s]=;
pre[s]=;incf[s]=inf;
q.push(s);
while(!q.empty()){
int x=q.front();q.pop();
vis[x]=;
for(int i=hd[x];i;i=e[i].nxt){
if(!e[i].w) continue;
int y=e[i].to;
if(dis[y]>dis[x]+e[i].c){
dis[y]=dis[x]+e[i].c;
pre[y]=i;
incf[y]=min(incf[x],e[i].w);
if(!vis[y]){
vis[y]=;
q.push(y);
}
}
}
}
if(dis[t]==inf) return false;
return true;
}
int ans;
void upda(){
int x=t;
bool fl=false;
while(x!=s){
//cout<<" xx "<<x<<" dis "<<dis[x]<<endl;
if(!fl&&x>=+n+&&x<=+n+sum*m){
int k=(x-n-+m-)/m; int num=(x-n--)%m+;
//cout<<" kkk "<<k<<" "<<num<<endl;
if(k<sum){
add(x+m,t,,);
//add(x+m+m,t,1,0);
for(int i=;i<=n;i++){
add(i+,x+m,,(k+)*tr[i][num]);
}
}
fl=true;
}
e[pre[x]].w-=incf[t];
e[pre[x]^].w+=incf[t];
x=e[pre[x]^].to; }
ans+=incf[t]*dis[t];
//cout<<" ----------------------after after "<<ans<<endl;
}
int getfood(int x){
return x+;
}
int getchef(int p,int x){
return +n+(p-)*m+x;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
scanf("%d",&p[i]);sum+=p[i];
}
s=,t=+n+sum*m+;
int tim;
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
scanf("%d",&tim);
tr[i][j]=tim;
add(getfood(i),getchef(,j),,*tim);
}
}
for(int i=;i<=n;i++){
add(s,getfood(i),p[i],);
}
for(int i=;i<=m;i++){
//for(int j=1;j<=sum;j++){
add(getchef(,i),t,,);
//}
}
while(spfa())upda();
printf("%d",ans);
return ;
}
总结:
动态处理无处不在。
都借助了均摊或者总和的复杂度比较低的特点,达到节省空间和时间的目的。
删除了许多并不会用到的节点或者是边。
[NOI2012]美食节——费用流(带权二分图匹配)+动态加边的更多相关文章
-
费用流模板(带权二分图匹配)——hdu1533
/* 带权二分图匹配 用费用流求,增加源点s 和 汇点t */ #include<bits/stdc++.h> using namespace std; #define maxn 1000 ...
-
运动员最佳匹配问题 KM算法:带权二分图匹配
题面: 羽毛球队有男女运动员各n人.给定2 个n×n矩阵P和Q.P[i][j]是男运动员i和女运动员j配对组成混合双打的男运动员竞赛优势:Q[i][j]是女运动员i和男运动员j配合的女运动员竞赛优势. ...
-
POJ 2195 Going Home (带权二分图匹配)
POJ 2195 Going Home (带权二分图匹配) Description On a grid map there are n little men and n houses. In each ...
-
POJ 2195 Going Home | 带权二分图匹配
给个地图有人和房子 保证人==房子,每个人移动到房子处需要花费曼哈顿距离的代价 问让人都住在房子里最小代价 显然是个带权二分图最大匹配 转化成以一个网络,规定w是容量,c是代价 1.S向人连边,w=1 ...
-
hdu5045:带权二分图匹配
题目大意 : n个人 做m道题,其中 每连续的n道必须由不同的人做 已知第i人做出第j题的概率为pij,求最大期望 思路:考虑每连续的n道题 都要n个人来做,显然想到了带权的二分图匹配 然后就是套模板 ...
-
[NOI2012]美食节(费用流)
题目描述 CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食节所有的美食.然而,尝鲜的欲望是难以满足的.尽管所有的菜品都 ...
-
【bzoj2879】[Noi2012]美食节 费用流+动态加边
原文地址:http://www.cnblogs.com/GXZlegend 题目描述 CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他 ...
-
BZOJ 2879: [Noi2012]美食节( 费用流 + 动态加边 )
倒着做菜..然后考虑为当前的人做菜对后面的人的影响就可以了..要动态加边 --------------------------------------------------------------- ...
-
[BZOJ2879] [Noi2012] 美食节 (费用流 &; 动态加边)
Description CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食节所有的美食.然而,尝鲜的欲望是难以满足的.尽 ...
随机推荐
-
IEEE754、VAX、IBM浮点型介绍和.NET中互相转换
[题外话] 最近在做C3D文件的解析,好奇怪的是文件中竟然存储了CPU的类型,原本不以为然,结果后来读取一个文件发现浮点数全部读取错误.查了下发现虽然在上世纪80年代就提出了IEEE754要统一浮点数 ...
-
问题解决——XP线程池找不到QueueUserWorkItem
2013年7月11号 主管让同事写一个并发100的小工具进行什么压力测试,据说是创建100个线程. 我表示这真真的是在坑人! 线程创建消耗资源,以自己的笔记本来跑这个东西,时间片都消耗在了线程切换上了 ...
-
2句代码轻松实现WPF最大化不遮挡任务栏并且具有边框调节效果
原文:2句代码轻松实现WPF最大化不遮挡任务栏并且具有边框调节效果 相信刚入门的菜鸟跟我一样找遍了百度谷歌解决最大化遮挡任务栏的方法大多方法都是HOOK一大堆API声明 最近在敲代码的时候无意中发现有 ...
-
老王说JavaDoc
开场白说点东西: { 抓住客户的痛点.痒点.爽点,提出我们产品的核心价值. 产品定位 技术架构 以微服务为核心的前后端分离,业务积木装配式技术架构.传感器采集,物联网+互联网转换,大数据分布式.存储. ...
-
关于 extern ";C";的说明
在用C++的项目源码中,经常会不可避免的会看到下面的代码 #ifdef __cplusplus extern "C" { #endif /*...*/ #ifdef __cplus ...
-
Docker环境安装与配置
Docker 简介 Docker使用Go语言编写的 安装Docker推荐LInux内核在3.10上 在2.6内核下运行较卡(CentOS 7.X以上内核是3.10) Docker 安装 安装yum-u ...
-
Django之集合函数使用与mysql表的创建特殊字段分析
1. 集合函数的使用场景: -- 单独使用: 不分组, 只查聚合结果 -- 分组使用: 按字段分组, 可查询分组字段与聚合结果 2. 导入聚合函数 from django.db.models impo ...
-
【LG4585】[FJOI2015]火星商店问题
[LG4585][FJOI2015]火星商店问题 题面 bzoj权限题 洛谷 \(Notice:\) 关于题面的几个比较坑的地方: "一天"不是一个操作,而是有0操作就相当于一天开 ...
-
Spring Cloud Config
1.config服务端配置 1.1 引入依赖 <dependency> <groupId>org.springframework.boot</groupId> &l ...
-
利用MyFlash闪回丢失数据(续)
last night,i've tested flashback by MyFlash tool,but failed,now let's do some other test with ...