【单调队列优化DP】 POJ 1821 Fence

http://poj.org/problem?id=1821

Fence

Time Limit: 1000MS Memory Limit: 30000K
Total Submissions:6917 Accepted: 2306

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.

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 

Source

Romania OI 2002

题意:给你n个工匠,每个工匠在s位置,如果要用,必须刷一个包含s位置的长度不超过l的连续区间,所得价值就是所刷长度*a[i].p,每块砖最多只能被刷一次,问你最大价值是多少

题解:首先dp[i][j]代表第i个工匠刷到第j面墙的最大价值是多少

因为dp[i][j]=max(dp[i-1][j],dp[i][j-1]).dp[i][j]=max(dp[i][j],max(dp[i-1][k]+(j-k)*a[i].p))(a[i].s-a[i].l<=j<=a[i].s+a[i].l&&j-k<=l)

这个max可以转化成优先队列来取最大值

max(dp[i-1][k]+(j-k)*a[i].p)=max(dp[i-1][k]-k*a[i].p)+j*a[i].p

我们将a[i].s前班位置刷的状况枚举,存入优先队列中,价值和刷的地方,然后枚举后半部分,找到符合条件的最大值进行更新

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=2e4+10;
int dp[105][maxn],n,k;
struct paint
{
    int l,p,s;
}a[maxn];

bool cmp(const paint &A,const paint &B)
{
    return A.s<B.s;
}

struct node
{
    int val,id;
    node(int _val,int _id)
    {
        val=_val,id=_id;
    }
};

bool operator<(const node &A,const node &B)
{
    //if(A.val==B.val) return A.id>B.id;
    return A.val<B.val;
}

priority_queue <node> q;

int main()
{
    scanf("%d%d",&k,&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&a[i].l,&a[i].p,&a[i].s);
    }
    sort(a+1,a+n+1,cmp);

    for(int i=1;i<=n;i++)
    {
        while(!q.empty()) q.pop();
        for(int j=max(0,a[i].s-a[i].l);j<a[i].s;j++) //刷前半部分,因为如果要刷,a[i].s一定要刷
        {
            q.push(node(dp[i-1][j]-j*a[i].p,j+1)); //从 j+1开始刷到a[i].s
        }
       // cout<<i<<endl;
        for(int j=1;j<=k;j++) //刷后半部分
        {
            dp[i][j]=max(dp[i-1][j],dp[i][j-1]); //更新
            if(j<a[i].s||j>=a[i].l+a[i].s) continue; //根本刷不到,就continue
            while(!q.empty()&&(j-q.top().id+1)>a[i].l) q.pop(); //刷的超过最多能刷的,删除
            if(q.empty()) continue; //空了不能取
            dp[i][j]=max(dp[i][j],q.top().val+j*a[i].p); //刷后半部分,从前半部分最大的承接过来
            //cout<<i<<" "<<j<<" "<<dp[i][j]<<endl;
        }

    }
    printf("%d\n",dp[n][k]);

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值