P2577 [ZJOI2005]午餐

题面

题目描述

上午的训练结束了, \(THU \ ACM\) 小组集体去吃午餐,他们一行 \(N\) 人来到了著名的十食堂。这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭。由于每个人的口味(以及胃口)不同,所以他们要吃的菜各有不同,打饭所要花费的时间是因人而异的。另外每个人吃饭的速度也不尽相同,所以吃饭花费的时间也是可能有所不同的。

\(THU \ ACM\) 小组的吃饭计划是这样的:先把所有的人分成两队,并安排好每队中各人的排列顺序,然后一号队伍到一号窗口去排队打饭,二号队伍到二号窗口去排队打饭。每个人打完饭后立刻开始吃,所有人都吃完饭后立刻集合去六教地下室进行下午的训练。

现在给定了每个人的打饭时间和吃饭时间,要求安排一种最佳的分队和排队方案使得所有人都吃完饭的时间尽量早。

假设 \(THU \ ACM\) 小组在时刻 \(0\) 到达十食堂,而且食堂里面没有其他吃饭的同学(只有打饭的师傅)。每个人必须而且只能被分在一个队伍里。两个窗口是并行操作互不影响的,而且每个人打饭的时间是和窗口无关的,打完饭之后立刻就开始吃饭,中间没有延迟。

现在给定 \(N\) 个人各自的打饭时间和吃饭时间,要求输出最佳方案下所有人吃完饭的时刻。

输入输出格式

输入格式:

第一行一个整数 \(N\) ,代表总共有 \(N\) 个人。

以下 \(N\) 行,每行两个整数 \(A_i\) , \(B_i\) 。依次代表第 \(i\) 个人的打饭时间和吃饭时间。

输出格式:

一个整数 \(T\) ,代表所有人吃完饭的最早时刻。

输入输出样例

输入样例:

5
2 2
7 7
1 3
6 4
8 5

输出样例:

17

说明

所有输入数据均为不超过 \(200\) 的正整数。

思路

哇 \(alec\) 你 \(AC\) 了,教我做吧! --Uranus

我看的题解。 --alec

alec巨佬学习动规中...

首先贪心地思考这道题:如果只有一个窗口,不同打饭时间和吃饭时间的同学要来吃饭,那么所有人都吃完饭的最早时刻怎么求?很容易发现,我们尽量使吃饭最慢的人早打饭,可以提高效率。所以先按照吃饭时间将同学们排序。(不是按照先来后到顺序,真不公平)

那么如何来动规呢?考虑设计状态 \(f[i][j][k]\) 表示前 \(i\) 个同学全部吃上饭,第一个窗口打饭时间为 \(j\) ,第二个窗口打饭时间为 \(k\) 时所有人都吃完饭的最早时刻。

很快发现, \(j+k= \Sigma ^{i}_{p=1}a[p]\) ,而对于每一个 \(i\) , \(\Sigma ^{i}_{p=1}a[p]\) 是固定的。所以我们可以统计前缀和,消掉状态的第三维,进行转移。

同理,我们也可以通过知道 \(j\) 和 \(k\) ,直接计算出 \(i\) ,以此为依据消去第一维,但是此写法过于毒瘤。

转移按照以下方法:

for(int i=1;i<=n;i++)
for(int j=0;j<=sum[i];j++)//sum为统计的前缀和
{
if(node[i].a<=j) f[i][j]=min(f[i][j],max(f[i-1][j-node[i].a],j+node[i].b));//在一号窗口打饭
f[i][j]=min(f[i][j],max(f[i-1][j],sum[i]-j+node[i].b));//在二号窗口打饭
}

那么我们就可以顺利 \(AC\) 了。

AC代码

#include<bits/stdc++.h>
using namespace std;
int n,ans=INT_MAX,f[205][40005],sum[205];
struct Node
{
int a,b;
bool operator < (const Node &sjf) const {return b>sjf.b;}
}node[205];
int read()
{
int re=0;
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
return re;
}
int main()
{
n=read();
for(int i=1;i<=n;i++) node[i].a=read(),node[i].b=read();
sort(node+1,node+n+1);
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+node[i].a;
memset(f,0x3f,sizeof f);
f[0][0]=0;
for(int i=1;i<=n;i++)
for(int j=0;j<=sum[i];j++)
{
if(node[i].a<=j) f[i][j]=min(f[i][j],max(f[i-1][j-node[i].a],j+node[i].b));
f[i][j]=min(f[i][j],max(f[i-1][j],sum[i]-j+node[i].b));
}
for(int i=0;i<=sum[n];i++) ans=min(ans,f[n][i]);
printf("%d",ans);
return 0;
}

最新文章

  1. Retina Display and Eclipse Mac视网膜屏和Eclipse
  2. Sql order by 和 group BY一起使用时需要注意
  3. 在Spring项目中使用Log4j记录日志
  4. ELK&mdash;&mdash;Elasticsearch 搭建集群经验
  5. Android 反编译apk 详解
  6. Ubuntu Qt arm-linux-androideabi-gcc: Command not found
  7. Linux常用命令查看日志
  8. gdb基本使用方法
  9. Qt网络通信骨架解析,QtClient QtServer QtSerialPort
  10. iOS8中的UIActionSheet添加UIDatePicker后,UIDatePicker不显示问题
  11. MySQL【第二篇】基本命令
  12. CentOS下MySQL 5.7编译安装
  13. jdk1.6,jdk1.7共存
  14. spring框架总结(01)
  15. 3D 散点图的绘制
  16. list常用方法
  17. 用惯图形界面的SVNer,如何突破Git----简单教程
  18. Keras 中 TimeDistributed 和 TimeDistributedDense 理解
  19. win32 窗口缩放时出现闪屏
  20. (转)Python新手写出漂亮的爬虫代码2——从json获取信息

热门文章

  1. &lt;Django&gt; 高级(其他知识点)
  2. HYNB Round 15: PKU Campus 2019
  3. Linux-c对一个十六进制数的某一位取反
  4. 全面理解python中self的用法
  5. 0906NOIP模拟测试赛后总结
  6. 菜鸟nginx源码剖析数据结构篇(一)动态数组ngx_array_t[转]
  7. day 41 前端之前端初识
  8. JQValidate使用说明
  9. [NOI2015] 软件包管理器【树链剖分+线段树区间覆盖】
  10. linux系统下重要的分区及其作用