[ZJOI2004]午餐
绿题。考场上想出了结论但不敢用。没往那方面去思考。太弱了。
按照样例可以猜想一个结论,即等待时间久的人一定会先去打饭。可以简要证明:假如相邻的两个人i和j,很明显他们之间的顺序不会影响到其他人的打饭时间,那么就看一下他们二人后吃完饭的时间。假设Bi>Bj,i先吃,则吃完饭的时间为 max(Ai+Bi,Ai+Aj+Bj),而j先吃,吃完饭的时间应该是Aj+Ai+Bi,后者肯定比前者大,所以相邻两个人的b值是有序的。既然任意相邻的元素是不降的,那么序列整体也应该是不降的。
既然有了这样一个结论,就可以考虑DP,令人欣慰的是这道题时间范围比较小,四万左右,可以作为一维加入状态。所以就捏出了一个fij的状态,表示前i个人打完饭了,第一个窗口所有人的打饭时间和为j时两个窗口最后一个人的吃完饭时间。转移方程就很简单了,毕竟决策只有两个,最后一个人只有两个选择的嘛。
#include<bits/stdc++.h>
//#define zczc
const int N=210;
using namespace std;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
inline void check(int &s1,int s2){
if(s1>s2)s1=s2;return;
}
inline int max(int s1,int s2){
return s1<s2?s2:s1;
}
struct node{
int a,b;
}a[N];
inline bool operator <(node s1,node s2){
return s1.b>s2.b;
}
int m,sum[N],f[N][N*N];
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
read(m);
for(int i=1;i<=m;i++){
read(a[i].a);read(a[i].b);
}
sort(a+1,a+m+1);
for(int i=1;i<=m;i++)sum[i]=sum[i-1]+a[i].a;
memset(f,0x3f,sizeof(f));
f[0][0]=0;
//for(int i=0;i<=10;i++)printf("%d ",min(f[0][i],99));printf("\n");
for(int i=1;i<=m;i++){
for(int j=0;j<=sum[i];j++){
//last one in A
if(j>=a[i].a)check(f[i][j],max(f[i-1][j-a[i].a],j+a[i].b));
//last one in B
check(f[i][j],max(f[i-1][j],sum[i]-j+a[i].b));
//printf("%d ",min(f[i][j],99));
}
//printf("\n");
}
int ans=1e9;
for(int i=0;i<=sum[m];i++)check(ans,f[m][i]);
printf("%d",ans);
return 0;
}
一如既往,万事胜意