题目
思路
显然的dp方程:(设dp[i][j]为前i个人干前j个方块的最大值)
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
−
1
]
,
d
p
[
i
−
1
]
[
j
]
,
m
a
x
(
d
p
[
i
−
1
]
[
k
]
+
p
[
i
]
∗
(
j
−
k
)
(
m
a
x
(
0
,
s
[
i
]
−
l
[
i
]
)
<
=
k
<
=
m
i
n
(
j
,
s
[
i
]
−
1
)
)
dp[i][j]=max(dp[i][j-1],dp[i-1][j],max(dp[i-1][k]+p[i]*(j-k)(max(0,s[i]-l[i])<=k<=min(j,s[i]-1))
dp[i][j]=max(dp[i][j−1],dp[i−1][j],max(dp[i−1][k]+p[i]∗(j−k)(max(0,s[i]−l[i])<=k<=min(j,s[i]−1))
发现第3个部分可以优化,化为
d
p
[
i
−
1
]
[
k
]
−
p
[
i
]
∗
k
+
p
[
i
]
∗
j
dp[i-1][k]-p[i]*k+p[i]*j
dp[i−1][k]−p[i]∗k+p[i]∗j,我们按顺序枚举时
p
[
i
]
∗
j
p[i]* j
p[i]∗j已经确定,所以我们要求前面最大就行了(单调队列优化)
code:
#include<iostream>
#include<algorithm>
#include<deque>
#include<cstdio>
using namespace std;
int n,k,dp[110][16010];
struct f{
int l,p,s;
} p[110];
bool cmp(f a,f b)
{
return a.s<b.s;
}
deque<int> op[110];
int main()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=k;i++) scanf("%d%d%d",&p[i].l,&p[i].p,&p[i].s);
sort(p+1,p+k+1,cmp);
for (int i=1;i<=k;i++)
{
op[i].push_back(max(0,p[i].s-p[i].l));
for (int j=1;j<=n;j++)
{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
if (j>=p[i].l+p[i].s) continue;
while (!op[i].empty()&&op[i].front()+p[i].l<j) op[i].pop_front();
if (j<p[i].s)
{
while (!op[i].empty()&&dp[i-1][op[i].back()]-op[i].back()*p[i].p<dp[i-1][j]-j*p[i].p) op[i].pop_back();
op[i].push_back(j);
continue;
}
dp[i][j]=max(dp[i][j],dp[i-1][op[i].front()]+p[i].p*(j-op[i].front()));
}
}
printf("%d",dp[k][n]);
return 0;
}