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 }

 

posted @ 2019-04-12 18:01  IAT14  阅读(169)  评论(0编辑  收藏  举报