poj1821 Fence

Description A team of k (1 <= K <= 100) workers should paint a fence
which contains N (1 <= N <= 16 000) planks numbered from 1 to N from
left to right. Each worker i (1 <= i <= K) should sit in front of the
plank Si and he may paint only a compact interval (this means that the
planks from the interval should be consecutive). This interval should
contain the Si plank. Also a worker should not paint more than Li
planks and for each painted plank he should receive Pi $ (1 <= Pi <=
10 000). A plank should be painted by no more than one worker. All the
numbers Si should be distinct.

Being the team’s leader you want to determine for each worker the
interval that he should paint, knowing that the total income should be
maximal. The total income represents the sum of the workers personal
income.

Write a program that determines the total maximal income obtained by
the K workers.

Input The input contains: Input

N K L1 P1 S1 L2 P2 S2 … LK PK SK

Semnification

N -the number of the planks; K ? the number of the workers Li -the
maximal number of planks that can be painted by worker i Pi -the sum
received by worker i for a painted plank Si -the plank in front of
which sits the worker i

Output The output contains a single integer, the total maximal income.

设dp[i][j]表示前i个工人刷前j面墙【可以有人不刷,也可以有墙不刷】的最大收益。
考虑朴素的dp方程,
dp[i][j]=max{dp[i-1][j],
dp[i][j-1],
dp[i-1][k]+p[i]*(j-k)|(j>=s[i]&&j<=s[i]+l[i]-1&&j-k+1<=l[i]&&k < s[i])}。
【分别的含义为:第i个人不刷,第j面墙不刷,第i个人刷k+1..j的墙。】
但是注意,这个方程应用的条件是dp[i-1][k]已经计算过了【更准确的说法是,能在这个地方涂的人都涂过了】。而很容易知道,s靠左的,最后涂的区域一定靠左,不会出现交叉的情况。所以要先按照s进行排序在按顺序dp。
前两个都可以O(1)转移,但是第三个转移需要O(m)。
dp[i-1][k]+p[i] * (j-k)=p[i] * j+dp[i-1][k]-p[i]*k
记f(k)=dp[i-1][k]-p[i]*k。
则原式=p[i]*j+f(k)。
这一式子前一项与k无关,后一项在i确定的情况下只和k有关,而随着j的增加,k的上界不变而下界增加,因此可以采用单调队列优化。
具体做法是在每个i确定时维护一个k递增、f(k)递减的单调队列。
但是这个单调队列和类似“最大子序和”【见这里】不同的是,k的上界保持不变,而不是随着j的增加而增加。
所以,入队的操作不应该随着j进行,而应该在一开始全部入队,再随着j的增加逐渐出队。

#include
#include
#include
using namespace std;
int max(int x,int y)
{
    return x>y?x:y;
}
struct worker
{
    int l,p,s;
    bool operator < (const worker &a) const
    {
        return s110];
int dp[110][16010],que[16010];
int fun(int i,int k)
{
    return dp[i-1][k]-a[i].p*k;
}
int main()
{
    int i,j,k,m,n,q,x,y,z,hd,tl;
    scanf("%d%d",&m,&n);
    for (i=1;i<=n;i++)
      scanf("%d%d%d",&a[i].l,&a[i].p,&a[i].s);
    sort(a+1,a+n+1);
    for (i=1;i<=n;i++)
    {
        hd=1;
        tl=0;
        for (j=0;j<=a[i].s-1;j++)
        {
            while (hd<=tl&&fun(i,que[tl])<=fun(i,j)) tl--;
            que[++tl]=j;
        }
        for (j=1;j<=m;j++)
        {
            dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            if (j>=a[i].s&&j<=a[i].s+a[i].l-1)
            {
                while (hd<=tl&&j-que[hd]>a[i].l) hd++;
                if (hd<=tl)
                  dp[i][j]=max(dp[i][j],a[i].p*j+fun(i,que[hd]));
            }
        }
    }
    printf("%d\n",dp[n][m]);
} 

你可能感兴趣的:(动态规划,数据结构)