poj1821 Fence,dp

来源:互联网 发布:织梦手机网站源码 编辑:程序博客网 时间:2024/05/22 03:49

poj1821 Fence

题意,给k个worker,n个plank,每个worker有l(刷plank的连续区间最长长度)、p(刷一个plank的工资)、s(这个worker坐在哪个plank前面)。问最大工资总和是多少。要求每个工人刷的plank是连续的,且要包含s位置的plank。


dp[i]表示做到第i个plank的最大值。
做每个worker时,将第i个位置的dp值加上(n-i)*p[i],这样,转移的时候只要求区间最大值即可。
将i按s[i]从小到大排序,转移过程大致如下:
for(i=1..k){
   for(j=s[i]+l[i]-1..s[i]){
      pos=querymax(j-l[i]..s[i]-1);
      dp[j]=max(dp[j],dp[pos]+(j-pos)*p[i]);
   }
}
然后我用线段树求最大值。算法复杂度O(knlog(n))
用G++TLE了两发,换成C++才935ms卡过。
我意识到应该会有更好的解法了。

看了一下discuss,大概是用单调队列。
想一下,我们按j从大到小dp,这样只要维护一个区间的最大值,只要维护一个位置从大到小,dp值也从大到小的队列即可。因为位置靠后,而值又小的总是不可能用来转移的。
通过双向队列,每次插入一个值,维护队尾即可,弹出队头即可。转移只要取队头。

再想了一下……感觉被耍了。
每次可能转移的区间只有在j-l[i]..s[i]-1,上界是不变的,下界一直在增加。这就根本没有队头弹出什么事了。
那还要毛单调队列,直接保存最大值,每次下界减一,拿下界位置的值来比较就可以了。
时间复杂度O(nk)。

#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>#include<queue>using namespace std;#define Maxn 16010int f[Maxn],n,wn,sv[Maxn];struct work{int l,p,s;}w[Maxn];bool cmp(work a,work b){return a.s<b.s;}int main(){    //freopen("1821in.txt","r",stdin);    int i,j,jb,je,k,ans,tmp,tj;    while(scanf("%d%d",&n,&wn)!=EOF){        n++;        for(i=1;i<=wn;++i){            scanf("%d%d%d",&w[i].l,&w[i].p,&w[i].s);            w[i].s+=1;        }        sort(w+1,w+wn+1,cmp);        memset(f,0,sizeof(f));        for(i=1;i<=wn;++i){            tmp=0;            for(j=n;j>=1;--j){                sv[j]=f[j]+tmp;                tmp+=w[i].p;            }            jb=w[i].s;            je=min(w[i].s+w[i].l-1,n);            tmp=0;            for(j=w[i].s-1;j>1&&j>je-w[i].l;--j){                if (sv[j]>tmp) {tmp=sv[j];k=j;}            }            tj=j+1;            for(j=je;j>=jb;--j){                if (tj>1){                    tj--;                    if (sv[tj]>tmp) {tmp=sv[tj];k=tj;}                }                f[j]=max(f[j],f[k]+(j-k)*w[i].p);            }            for(j=jb;j<=n;++j) if (f[j-1]>f[j]) f[j]=f[j-1];        }        ans=f[n];        printf("%d\n",ans);    }    return 0;}

线段树脑残版本,请用C++。

#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>using namespace std;#define ls (p<<1)#define rs (p<<1|1)#define Maxn 16010int f[Maxn],n,wn;struct node{    int pos,val;    bool operator <(const node &b)const{        return val<b.val;    }};struct segtree{    int l,r;    node d;}t[Maxn*4];void build(int l,int r,int p,int val){    t[p].l=l;t[p].r=r;    if (l==r){t[p].d.val=(n-l)*val+f[l];t[p].d.pos=l;return;}    int m=l+r>>1;    build(l,m,ls,val);    build(m+1,r,rs,val);    if (t[rs].d<t[ls].d) t[p].d=t[ls].d;    else t[p].d=t[rs].d;}node qmax(int l,int r,int p){    if (t[p].l==l&&t[p].r==r) return t[p].d;    int m=t[p].l+t[p].r>>1;    if (r<=m) return qmax(l,r,ls);    else if (l>m) return qmax(l,r,rs);    else{        node nd1,nd2;        nd1=qmax(l,m,ls);        nd2=qmax(m+1,r,rs);        if (nd2<nd1) return nd1;        else return nd2;    }}struct work{int l,p,s;}w[Maxn];bool cmp(work a,work b){return a.s<b.s;}int main(){    //freopen("1821in.txt","r",stdin);    int i,j,jb,je,k,ans;    node td;    while(scanf("%d%d",&n,&wn)!=EOF){        n++;        for(i=1;i<=wn;++i){            scanf("%d%d%d",&w[i].l,&w[i].p,&w[i].s);            w[i].s+=1;        }        sort(w+1,w+wn+1,cmp);        memset(f,0,sizeof(f));        for(i=1;i<=wn;++i){            build(1,n,1,w[i].p);            jb=w[i].s;            je=min(w[i].s+w[i].l-1,n);            for(j=je;j>=jb;--j){                td=qmax(max(j-w[i].l,1),w[i].s-1,1);                k=td.pos;                f[j]=max(f[j],f[k]+(j-k)*w[i].p);            }            for(j=2;j<=n;++j) if (f[j-1]>f[j]) f[j]=f[j-1];        }        ans=f[n];        printf("%d\n",ans);    }    return 0;}




0 0