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
- poj1821 Fence,dp
- poj1821 Fence 单调队列dp
- 【poj1821】Fence 单调队列优化DP
- [poj1821] Fence DP单调队列优化
- poj1821 Fence
- POJ1821-Fence
- POJ1821-Fence
- POJ1821-Fence
- 单调队列 poj1821 Fence
- 单调队列优化dp [HDU2191][HDU3401][POJ1821]
- 单调队列及优化DP poj2823/poj1821/poj2373
- 234F fence (DP)
- 【DP POJ 1821】Fence
- codeforces Wooden Fence 简单DP
- codeforces 240B - Fence DP
- codeforces 234F Fence (dp)
- POJ1037:A decorative fence(DP)
- 【dp】leetcode 276. Paint Fence
- ofstream/ifstream 文本/二进制 方式 读入/写出 数据方法
- 我的ios开发
- TCP、UDP和HTTP
- 基于S3C2440和嵌入式Linux的扩展串口设计
- oracle系统查询
- poj1821 Fence,dp
- 在创建AVD时无法找到Use host GPU的问题
- Java---Socket编程UDP/TCP
- 这是第一篇
- 微信使用篇 - 如何在订阅号与服务号之间做出选择
- 703n路由器 openwrt 串口 不能传送 0x11 0x13
- 09、maven插件管理
- seL4 调试指南
- struts2 中的 addActionError 、addFieldError、addActionMessage的方法【转】