POJ - 1821 ——Fence(dp+优先队列)

题目链接: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]);
    }
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值