首页 > 其他分享 >【题解】P2050 [NOI2012] 美食节

【题解】P2050 [NOI2012] 美食节

时间:2022-12-12 14:45:13浏览次数:44  
标签:int 题解 tail 40 P2050 厨师 等待时间 菜品 美食节

[NOI2012] 美食节

题目描述

CZ 市为了欢迎全国各地的同学,特地举办了一场盛大的美食节。

作为一个喜欢尝鲜的美食客,小 M 自然不愿意错过这场盛宴。他很快就尝遍了美食节所有的美食。然而,尝鲜的欲望是难以满足的。尽管所有的菜品都很可口,厨师做菜的速度也很快,小 M 仍然觉得自己桌上没有已经摆在别人餐桌上的美食是一件无法忍受的事情。于是小 M 开始研究起了做菜顺序的问题,即安排一个做菜的顺序使得同学们的等待时间最短。

小 M 发现,美食节共有 \(n\) 种不同的菜品。每次点餐,每个同学可以选择其中的一个菜品。总共有 \(m\) 个厨师来制作这些菜品。当所有的同学点餐结束后,菜品的制作任务就会分配给每个厨师。然后每个厨师就会同时开始做菜。厨师们会按照要求的顺序进行制作,并且每次只能制作一人份

此外,小 M 还发现了另一件有意思的事情——虽然这 \(m\) 个厨师都会制作全部的 \(n\) 种菜品,但对于同一菜品,不同厨师的制作时间未必相同。他将菜品用 \(1, 2, \ldots, n\) 依次编号,厨师用 \(1, 2, \ldots, m\) 依次编号,将第 \(j\) 个厨师制作第 \(i\) 种菜品的时间记为 \(t_{i,j}\)。

小 M 认为:每个同学的等待时间为所有厨师开始做菜起,到自己那份菜品完成为止的时间总长度。换句话说,如果一个同学点的菜是某个厨师做的第 \(k\) 道菜,则他的等待时间就是这个厨师制作前 \(k\) 道菜的时间之和。而总等待时间所有同学的等待时间之和

现在,小 M 找到了所有同学的点菜信息——有 \(p_i\) 个同学点了第 \(i\) 种菜品(\(i=1, 2, \ldots, n\))。他想知道的是最小的总等待时间是多少。

输入格式

输入文件的第 \(1\) 行包含两个正整数 \(n\) 和 \(m\),表示菜品的种数和厨师的数量。

第 \(2\) 行包含 \(n\) 个正整数,其中第 \(i\) 个数为 \(p_i\),表示点第 \(i\) 种菜品的人数。

接下来有 \(n\) 行,每行包含 \(m\) 个非负整数,这 \(n\) 行中的第 \(i\) 行的第 \(j\) 个数为 \(t_{i,j}\),表示第 \(j\) 个厨师制作第 \(i\) 种菜品所需的时间。

输入文件中每行相邻的两个数之间均由一个空格隔开,行末均没有多余空格。

输出格式

输出仅一行包含一个整数,为总等待时间的最小值。

样例 #1

样例输入 #1

3 2 
3 1 1 
5 7 
3 6 
8 9

样例输出 #1

47

提示

厨师 \(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 \leq 40\),\(m\leq 100\),\(p\leq 800\),\(t_{i,j}\leq 1000\)(其中 \(p = \sum p_i\))。

题解

此题显然可以想到一种思路是对于每个厨师做过菜之后我们可以更新所有连向它的边(使边权加上当前已做菜的时间),但是显然这种思路是错的,原因在于不能保证当前最优是全局最优。考虑网络流的实质是反悔贪心,上述做法错误即在于无法反悔,于是考虑不更新边权而是新建一个点把所有边新建出来,于此可满足反悔贪心。

同时有另一个思路,我们从前往后考虑每一道菜贡献了多少次,假设厨师\(a\)做了\(k\)道菜,那么他做的第一道菜会贡献 $ time_{第一道菜} \times k$ 的时间,于是可以对\(k\)建分层图跑。注意到图上实际有大量节点无用,于是可以跑完某厨师的某层再建当前厨师的下一层,时间复杂度优秀。

#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}

const int N=100001,M=5000000;
int head[N],to[M],fro[M],flo[M],val[M],tail=1;
int n,m,cost[51][101],tim[101],all,ans;
int s,t,pla[51],cook[101],cnt;
int fl[N],va[N],fr[N],fa[N];
int par[N];
bool inque[N];

inline void addlin(int x,int y,int a,int b){
	to[++tail]=y;
	fro[tail]=head[x];
	head[x]=tail;
	flo[tail]=a,val[tail]=b;
	to[++tail]=x;
	fro[tail]=head[y];
	head[y]=tail;
	flo[tail]=0,val[tail]=-b;
}
bool spfa(int last){
	for(int i=1;i<=cnt;i++){
		fl[i]=0;
		va[i]=INT_MAX/3;
	}
	fl[s]=last,va[s]=0;
	deque<int>p;
	p.push_back(s);
	while(!p.empty()){
		int u=p.front();p.pop_front();
		inque[u]=false;
		for(int k=head[u];k;k=fro[k]){
			int x=to[k];
			if(!flo[k]||va[x]<=va[u]+val[k])continue;
			va[x]=va[u]+val[k];
			fl[x]=min(fl[u],flo[k]);
			fr[x]=k,fa[x]=u;
			if(!inque[x])p.push_back(x),inque[x]=true;
		}
	}
	return fl[t]!=0;
}

signed main(){
	n=rd(),m=rd();
	s=++cnt,t=++cnt;
	for(int i=1;i<=n;i++){
		int x=rd();
		pla[i]=++cnt;
		addlin(s,pla[i],x,0);
		all+=x; 
	}
	for(int i=1;i<=m;i++){
		cook[i]=++cnt,tim[i]=1;
		par[cnt]=i;
		addlin(cook[i],t,1,0);
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++)cost[i][j]=rd();
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			addlin(pla[i],cook[j],1,cost[i][j]);
		}
	}
	while(spfa(all)){
		all-=fl[t];
		ans+=va[t]*fl[t];
		int u=par[fa[t]];
//		cout<<u<<":"<<fl[t]<<" "<<va[t]<<"\n";
		tim[u]++;
		cook[u]=++cnt;
		par[cook[u]]=u;
		addlin(cook[u],t,1,0);
		for(int i=1;i<=n;i++){
			addlin(pla[i],cook[u],1,cost[i][u]*tim[u]);
		}
		for(u=t;u!=s;u=fa[u]){
			flo[fr[u]]-=fl[t],flo[fr[u]^1]+=fl[t];
		}
	}
	printf("%d",ans);
	return 0;
}

标签:int,题解,tail,40,P2050,厨师,等待时间,菜品,美食节
From: https://www.cnblogs.com/T-water/p/16975995.html

相关文章

  • 2022icpc杭州铜牌题题解
    A.ModuloRuinstheLegend\[求s、d,使\suma_i+sn+d\frac{n(n+1)}{2}\(\bmodm)最小\\设sum=\suma_i\(\bmodm),t=gcd(n,\frac{n(n+1)}{2})\\原式=sum+kt\(\bm......
  • Git merge 报错:* commits behind * branch 问题解决
    Git大家都用的很多,但是在多人开发中难免会遇到代码冲突问题,因为mergepullrequest的时候遇到很多次这个问题,所以今天特意来记录一下: 问题:在mergePR到主分支(master/......
  • 【题解】P2774 方格取数问题
    方格取数问题题目描述有一个\(m\)行\(n\)列的方格图,每个方格中都有一个正整数。现要从方格中取数,使任意两个数所在方格没有公共边,且取出的数的总和最大,请求出最大的......
  • 【题解】P3358 最长k可重区间集问题
    最长k可重区间集问题题目描述给定实直线\(\text{L}\)上\(n\)个开区间组成的集合\(\mathbf{I}\),和一个正整数\(k\),试设计一个算法,从开区间集合\(\mathbf{I}\)中选......
  • 洛谷 P1786 帮贡排序 题解
    原题链接P1786帮贡排序解析实现方法一看题:这不就是道排序吗?但是——用啥办法呢?这自带的排序方法,肯定是不能用了那么我们就来写一个cmp排序函数吧!但是——输出排......
  • 题解 洛谷 P2885 [USACO07NOV]Telephone Wire G
    1.题面描述题目链接给出\(n\)棵树的高度。你可以进行任意次操作,每次操作形如:把某棵树增高\(x\),代价为\(x^2\)(注意:不能将一棵树的高度减少)。在操作完之后,一个状态......
  • 题解 洛谷 P3594 [POI2015] WIL
    1.题面描述题目链接给定一个长度为\(n\)的序列,你有一次机会选中一段连续的长度不超过\(d\)的区间,将里面所有数字全部修改为\(0\)。请找到最长的一段连续区间,使得该......
  • 题解 CodeForces 940E Cashback
    1.题面描述题目链接给定长为\(n\)的序列和一个整数\(c\),你需要将其分为若干段。对于每一段,若其长度为\(k\),则可以删除其中前\(\lfloor\frac{k}{c}\rfloor\)小的......
  • 题解 洛谷 P3793 由乃救爷爷
    1.题面描述题目链接给定长为\(n\)的序列,\(m\)次询问,查询区间最大值。\(n,m\le10^7\),数据随机。2.分析经典的静态区间最小值问题,经典题目配经典算法,考虑到ST表......
  • NOIP2022 题解
    A.种花枚举\((x_2,y_0)\),考虑\(x_1\)可能在哪些位置,显然是在\(x_2\)往上的一个极长连续0段上。考虑如果固定了\(x_1\)的位置后怎么计算C形的数量,我们预处理......