题目
思路
显然的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;
}