https://www.luogu.org/problemnew/show/P2577
题解
这其实是一个背包问题的变形,如果只有一个窗口的话,对于排队打饭的时间是固定的,那么只要按照谁吃的慢谁先上就可以得出最优值。
这道题有两个窗口,贪心的方法还是一样的,但是需要考虑分配问题。
设dp[i][j][k]: 下标:前i个人在窗口1花了j的打饭时间,在窗口2花了k的打饭时间。值: 前i个人最小的用餐时间。
设打饭时间为a,吃饭时间为b。
状态转移方程很容易写出来:
考虑决策:
- 第i个人打饭+吃饭的时间被前i-1个人的总时间覆盖。
- 第i个人打饭+吃饭的时间不被前i-1个人的总时间覆盖。
在窗口1打饭
d
p
[
i
]
[
j
]
[
k
]
=
m
i
n
(
d
p
[
i
]
[
j
]
[
k
]
,
m
a
x
(
d
p
[
i
−
1
]
[
j
−
a
]
[
k
]
,
j
+
b
)
)
dp[i][j][k] = min(dp[i][j][k], max(dp[i-1][j-a][k], j+b) )
dp[i][j][k]=min(dp[i][j][k],max(dp[i−1][j−a][k],j+b))
在窗口2打饭
d
p
[
i
]
[
j
]
[
k
]
=
m
i
n
(
d
p
[
i
]
[
j
]
[
k
]
,
m
a
x
(
d
p
[
i
−
1
]
[
j
]
[
k
−
a
]
,
j
+
b
)
)
dp[i][j][k] = min(dp[i][j][k], max(dp[i-1][j][k-a], j+b) )
dp[i][j][k]=min(dp[i][j][k],max(dp[i−1][j][k−a],j+b))
但由于数据量是200*40000*40000,所以三维开不下。
对于这种两个有关联的变量,就要考虑用一个变量推出另一个变量。
注意前面一个队伍的情况,打饭时间是固定的!
所以对于前i个人的打饭时间是固定的!那么只要知道花在窗口1多少打饭时间,自然就可以知道花在窗口2的打饭时间了。
因此便省去了一维。
设dp[i][j]: 下标:前i个人在窗口1花了j的打饭时间。值: 前i个人最小的用餐时间。
状态转移方程:
在窗口1打饭
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
m
a
x
(
d
p
[
i
−
1
]
[
j
−
a
]
,
j
+
b
)
)
dp[i][j] = min(dp[i][j], max(dp[i-1][j-a],j+b))
dp[i][j]=min(dp[i][j],max(dp[i−1][j−a],j+b))
在窗口2打饭
sum[i]:前i个人打饭时间的前缀和。
sum[i]-j:前i个人在队伍2的打饭时间。
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
m
a
x
(
d
p
[
i
−
1
]
[
j
]
,
s
u
m
[
i
]
−
j
+
b
)
)
dp[i][j] = min(dp[i][j], max(dp[i-1][j], sum[i]-j+b))
dp[i][j]=min(dp[i][j],max(dp[i−1][j],sum[i]−j+b))
代码
#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 200+5;
const int INF = 0x3f3f3f3f;
struct Peo {
int da, eat;
bool operator < (const Peo& rhs) const {
return eat > rhs.eat;
}
}a[maxn];
int dp[maxn][maxn*maxn],n, sum[maxn];
// 前i个人,在队伍1打饭耗时j 时总的最小用餐时间
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d%d", &a[i].da, &a[i].eat);
}
sort(a+1,a+1+n);
memset(dp, INF, sizeof dp);
dp[0][0] = 0;
for(int i = 1; i <= n; ++i)
sum[i] = sum[i-1]+a[i].da;
for(int i = 1; i <= n; ++i) {
for(int j = sum[i]; j >= 0; --j) {
// 排在队伍1
if(j >= a[i].da)
dp[i][j] = min(dp[i][j], max(dp[i-1][j-a[i].da], j+a[i].eat));
// 两个决策: 前一个人吃完饭的时间是否覆盖第i个人,不覆盖
// 排在队伍2
dp[i][j] = min(dp[i][j], max(dp[i-1][j], sum[i]-j+a[i].eat));
}
}
int ans = INF;
for(int i = 0; i <= sum[n]; ++i)
if(dp[n][i] < ans) ans = dp[n][i];
printf("%d\n", ans);
return 0;
}
234



被折叠的 条评论
为什么被折叠?



