Fence (POJ-1821)(单调队列+优化dp)

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.

Sample Input

8 4
3 2 2
3 2 3
3 3 5
1 1 7 

Sample Output

17

Hint

Explanation of the sample: 

the worker 1 paints the interval [1, 2]; 

the worker 2 paints the interval [3, 4]; 

the worker 3 paints the interval [5, 7]; 

the worker 4 does not paint any plank 

题意:有n块木板,m个工匠将其进行粉刷,木板至多被粉刷一次,工匠要么不粉刷,要么粉刷包含木板Si的、长度不超过Li的连续的一段木板,每粉刷一块可得Pi的报酬。求最大的总报酬。
思路:先把所有工匠按照Si排序,这样,每个工匠粉刷的木板一定在上一个工匠粉刷的木板之后。
           设dp[i,j]表示安排前i个工匠粉刷前j块木板,工匠们能获得的最多报酬:

              1. dp[i,j]=dp[i−1,j]
              2. dp[i,j]=dp[i,j−1] 
              3. dp[i,j]=max{dp[i−1,k]+Pi∗(j−k)} 其中j−Li≤k≤Si−1,j≥Si 
然后我们可以用单调队列维护一个决策点z单调递增,数值dp[i−1,z]−Pi∗z单调递减的队列。支持如下几种操作:

         1. 当j变大时,检查队头元素,把小于j−Li的决策出队。
         2. 需要查询最有决策时,队头即为所求。
         3. 有一个新的决策要加入候选集合时,在队尾检查dp[i−1,z]−Pi∗z的单调性,把无用决策从队尾直接出队,最后把新决策加入队列。

AC代码:

#include <stdio.h>
#include <iostream>
#include <string>
#include <string.h>
#include <algorithm>
#include <queue>
#include <stack>
const int maxx=20010;
const int inf=0x3f3f3f3f;
using namespace std;
int dp[110][maxx],q[maxx];
struct node
{
    int l,p,s;
} b[110];
int sc(int i,int j)
{
    return dp[i-1][j]-b[i].p*j;
}
bool cmp(node x,node y)
{
    return x.s<y.s;
}
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1; i<=k; i++)
        scanf("%d%d%d",&b[i].l,&b[i].p,&b[i].s);
    sort(b+1,b+k+1,cmp);
    for(int i=1; i<=k; i++)
    {
        int head=0,tail=0;
        for(int z=max(0,b[i].s-b[i].l); z<=b[i].s-1; z++)
        {
            while(head<=tail && sc(i,q[tail])<=sc(i,z)) tail--; //插入新决策,维护队尾单调性
            q[++tail]=z;
        }
        for(int j=1; j<=n; j++)
        {
            dp[i][j]=max(dp[i-1][j],dp[i][j-1]);//不粉刷时的转移
            if(j>=b[i].s)
            {
                while(head<=tail && j-b[i].l>q[head]) head++;//排除队头不合法决策
                if(head<=tail)
                    dp[i][j]=max(dp[i][j],sc(i,q[head])+b[i].p*j);//队列非空时,取队头进行状态转移
			}
            }
        }
    }
    printf("%d",dp[k][n]);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值