[ZJOI2004]午餐

link

绿题。考场上想出了结论但不敢用。没往那方面去思考。太弱了。

按照样例可以猜想一个结论,即等待时间久的人一定会先去打饭。可以简要证明:假如相邻的两个人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;
}
posted @ 2022-06-21 16:58  Feyn618  阅读(57)  评论(0编辑  收藏  举报