pku 1821神奇DP

废话不多说。dp方程很好写,dp[i][j]=max(dp[i-1][k]+cost[i]*(j-k))。当然也可以省略一维节省内存。然后这个N*K*N的算法必然TLE所以说要想优化。开始想的单调队列优化啊但是总想不清楚那个实现的过程。也想过要倒着枚举j,但是立即想到这前的k还没算出来呢。所以说被自己给否了。其实仔细想想就好了。对于第i个worker来说,它能够达到的最右边界是worker[i].s+worker[i].l-1。那么对于这个状态,它只能从dp[woker[i].s-1]这个状态转移过来。而对于下一个j来说,就只能从dp[worker[i].s-1]和dp[worker[i].s-2]这两个状态转移过来。而对于这两者中取得较大值的左边界来说,右边的j同时减1以后还是另下一个j取得最大值的解。而再跟dp[worker[i].s-3]进行比较的就应该是这个最大值了。所以说可以用一个数组记录下worker[i]的左边界,不断地更新它。因为是一次移动1,所以能够做到保证不重不漏。

注意一下初始化。我开始忘记了j-worker[i].l<=0的情况。你为什么就那么理所当然地认为减了以后还是大于0的呢???!!!

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 110;
const int maxm = 20000;
int n,k;
struct node
{
    int p,l,s;
}worker[maxn];
int res[maxn];
int dp[maxm];
inline bool cmp(const node&a,const node&b)
{
    return a.s<b.s;
}
int main()
{
    int i,j,t;
   // freopen("in2.txt","r",stdin);
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        worker[0].s = 0;
        for(i = 1;i<=k;i++)
          scanf("%d%d%d",&worker[i].l,&worker[i].p,&worker[i].s);
        sort(worker,worker+k+1,cmp);
        memset(dp,0,sizeof(dp));
       // int tmp = min(worker[1].l,worker[2].s-1);
        for(i = worker[1].s;i<=worker[1].l;i++)
           dp[i] = worker[1].p*i;
        for(i = 2;i<=k;i++)
        {
            res[i] = worker[i].s;
            if(dp[worker[i].s-1]>0&&dp[worker[i].s-1]+worker[i].p*worker[i].l>dp[t = worker[i].s+worker[i].l-1])
            dp[t] = dp[worker[i].s-1]+worker[i].p*worker[i].l;
           // printf("%d/n",t);
            for(j = t-1;j>=worker[i].s;j--)
            {
                if(res[i]-1>0&&dp[res[i]-1]>0&&dp[res[i]-1]+worker[i].p*(j-res[i]+1)>dp[j])
                   dp[j] = dp[res[i]-1]+worker[i].p*(j-res[i]+1);
                else if(res[i]-1<=0)dp[j] = max(dp[j],worker[i].p*j);
                if(j-worker[i].l>0&&dp[j-worker[i].l]>0&&dp[j-worker[i].l]+worker[i].p*worker[i].l>dp[j])
                  dp[j] = dp[j-worker[i].l]+worker[i].p*worker[i].l,res[i] = j-worker[i].l+1;
                else if(j-worker[i].l<=0&&worker[i].p*j>dp[j])
                   dp[j] = worker[i].p*j;//,res[i] = j-worker[i].l+1;
            }
        }
        int ans = 0;
        for(i = 1;i<=n;i++)
          ans = max(ans,dp[i]);
     /*   for(i = 1;i<=n;i++)
           printf("%d/n",dp[i]);*/

        printf("%d/n",ans);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值