luogu P2577 [ZJOI2005]午餐 序列DP
我们先考虑只有一个窗口的情况。如果只有一个窗口,显然我们应该按照吃饭的时间进行排序,按这个顺序先后打饭吃饭即可。因为窗口打饭时间的和是一定的,我们越让吃饭快的人靠后,最后的结束时间就可能更早。
那么对于两个窗口,我们依旧将人按照吃饭快慢排序。现在得到了一个序列,我们考虑序列DP。用f[i][t]表示,i及i以前的学生都吃完饭,并且一号窗口打了t时间的饭,的最后一个吃完饭的人的结束时间(两个窗口的人都算上)。我们对打饭时间维护一个前缀和sum[i],那么1号窗口打了t时间的饭,显然二号窗口打了sum[i] - t时间的饭。
我们考虑转移,对于dp[i][t],第i个人显然有两种打饭的选择,要么去1号窗口,要么去2号窗口。去一号窗口的话,最后的截至时间,就是max(dp[i - 1][t - a[i]],t - a[i] + a[i] + b[i])。注意我这里没有把a[i]消掉是便于理解。如果去二号窗口的话,就是max(dp[i - 1][t],sum[i - 1] -t - a[i] + a[i] + b[i])。
最后对于所有的f[n][t]取一个最小值即可。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 using namespace std; 5 struct peo 6 { 7 int a,b; 8 friend bool operator < (peo x,peo y) 9 { 10 if (x.b != y.b) 11 return x.b > y.b; 12 return x.a > y.a; 13 } 14 } vec[210]; 15 int n,res = 1000000; 16 int sum[210],f[210][41000]; 17 //f[i][t] 表示前i位同学都吃晚饭,1号打饭花了t时间的最早结束时间。 18 int main() 19 { 20 memset(f,0x1f,sizeof(f)); 21 f[0][0] = 0; 22 scanf("%d",&n); 23 for (int i = 1;i <= n;i++) 24 scanf("%d%d",&vec[i].a,&vec[i].b); 25 sort(vec + 1,vec + n + 1); 26 for (int i = 1;i <= n;i++) 27 sum[i] = vec[i].a + sum[i - 1]; 28 for (int i = 1;i <= n;i++) 29 for (int t = 0;t <= sum[i];t++) 30 if (t >= vec[i].a) 31 f[i][t] = min(max(f[i - 1][t],sum[i - 1] - t + vec[i].b + vec[i].a),max(f[i - 1][t - vec[i].a],t - vec[i].a + vec[i].a + vec[i].b)); 32 else 33 f[i][t] = max(f[i - 1][t],sum[i - 1] - t + vec[i].b + vec[i].a); 34 for (int t = 0;t <= sum[n];t++) 35 res = min(res,f[n][t]); 36 printf("%d\n",res); 37 return 0; 38 }
心之所动 且就随缘去吧