//My AC Code:
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n,dp[205][205*205];
int sum[205];
//n表示有多少个人要分两队打饭
//dp[i][j]表示 前i个人也表示选第i个人,在第一个窗口打饭的总时间j , 最早吃完饭的时间
struct node{
int ai,bi;
}nd[205];
/*
容易想到贪心:吃饭慢的先打饭节约时间, 所以先将人按吃饭时间从大到小排序。
然后就是dp了: 首先,应该想到f[i][j][k]:前i个人,在1号窗口打饭总时间j,在2号窗口打饭总时间k
当然,这样会爆空间,所以想到去掉一维。
f[i][j]表示前i个人,在1号窗口打饭总时间j,最早吃完饭的时间
我们可以发现 j+k=前i个人打饭总和,k = sum(i)-j。 所以可以维护一个前缀和
*/
bool cmp(node a,node b){return a.bi>b.bi;}
int min(int a,int b){return a>b?b:a;}
int max(int a,int b){return a>b?a:b;}
//按照吃饭时间 ,从大到小排序
/*
一看题目 就知道 是应该从前往后推的dp
所以 核心代码的第一个for是表示表示 选第i个人在哪号窗口打饭,或者是 前i个人 的意思
然后第二个循环 就是前i个人的打饭时间总和 一定要0 推到 sum[i] 因为 0的时候代表 不去1号窗口去了2号窗口,然后 就可以状态转移
最后 就是 要 判断 dp[n][i] 最小是哪个然后输出
*/
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d%d",&nd[i].ai,&nd[i].bi);
sort(nd+1,nd+1+n,cmp);
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+nd[i].ai;
memset(dp,1,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=sum[i];j++){
if(j>=nd[i].ai)dp[i][j]=min(dp[i][j],max(dp[i-1][j-nd[i].ai],j+nd[i].bi));
dp[i][j]=min(dp[i][j],max(dp[i-1][j],sum[i]-j+nd[i].bi));
}
}
int ans=2e9;
for(int i=0;i<=sum[n];i++){
ans=min(ans,dp[n][i]);
}
printf("%d",ans);
return 0;
}
P2577 [ZJOI2005]午餐
2019-05-10