题目链接:http://poj.org/problem?id=1821
题意:n块木板,m个工匠,每个工匠只能刷li块木板(木板必须连续),每刷一块木板得到pi块钱,坐在si位置(刷的木板必须包括si)。刷过的木板不能重复刷,求最多可以得到多少钱。比方si=5,li=3时,木匠能刷的 区域有【3,5】,【4,6】,【5,7】,只能选一个区域刷,可以不刷完整个区域。
思路:dp【i】【j】表示第i个木匠刷到第j块木板时能得到的最大金钱,一共三个状态。
第一个转移方程:不需要第i个木匠进行粉刷dp【i-1】【j】。
第二个转移方程:第i个木匠不需要对第j个木板进行粉刷dp【i】【j-1】。
第三个转移方程:第i个木匠对第j个木板进行粉刷dp【i-1】【k】+pi*(j-k)。表示上一个工人刷到第k个木板,第i个木匠接着从第k+1个木板刷大j。
需要对第三个状态转移方程进行优化,第三个方程化简为dp【i-1】【k】-pi*k+pi*j,因为pi*j是固定不变的,对于每个
dp【i-1】【k】-pi*k找出最大的,符合条件的就行了,用优先队列维护。
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<queue>
using namespace std;
struct nob
{
int v,p;
bool operator <(const nob &a)const
{
return this->v<a.v;
}
nob() {};
nob(int _v,int _p)
{
this->v=_v;
this->p=_p;
}
};
struct node
{
int l,p,s;
bool operator < (const node &a)const
{
return this->s<a.s;
}
} A[110];
int dp[110][16010];
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
for(int i=1; i<=m; i++)
scanf("%d%d%d",&A[i].l,&A[i].p,&A[i].s);
sort(A+1,A+m+1);
memset(dp,0,sizeof(dp));
for(int i=1; i<=m; i++)
{
priority_queue<nob>q;
int li=A[i].l,pi=A[i].p,si=A[i].s;
for(int j=max(0,si-li); j<si; j++)
{
q.push(nob(dp[i-1][j]-j*pi,j));
}
for(int j=1; j<=n; j++)
{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
if(j<si||j>=si+li)
continue;
while(!q.empty()&&q.top().p+li<j) q.pop();
if(q.empty())
continue;
dp[i][j]=max(dp[i][j],q.top().v+pi*j);
}
}
printf("%d\n",dp[m][n]);
}
}