-
D - Fence
- POJ - 1821
- 题意:现在有n个地板需要画,同时有k个工人分别坐在S[i]位置上。每个工人只能画他周围最多L[i]个位置,如果画必须画上S[i]位置,每画一个可得到P[i]的金钱。求总的最大金钱。
- 思路:dp[i][j] 表示前i个工人画到第j个地板所得到的最大金钱。
- 那么dp[i][j] = max(dp[i][j-1],dp[i-1][j])
- (假设当前第i个工人不画第j个位置,那么dp[i][j] 就等于前i个工人画到第j-1个或者前i-1个工人画到第j个的最大值),如果画上第j个位置,则dp[i][j] = max(dp[i-1][k]+(j-k)*P[i],dp[i][j]),k是枚举的上一个工人画到什么位置
- dp[i-1][k]+(j-k)*P[i]可以写成j*P[i] 与 dp[i-1][k]-k*P[i]的和而想办法枚举j时就能解决掉 dp[i-1][k]-k*P[i]这个式子的最大值
- 采用单调队列维护最大值,但是不可全部进入队列同时也不可全部进行这个dp[i][j]的更新,因为第i个人控制的范围有限
- 所以当j<num[i].s去单调队列维护k值,因为现在准备更新的是第i个人一定要涂了所以 前i-1个人所涂的不能超过num[i].s
- 单调队列不断维护最大值即可与此相对的是第i个人可以更新的范围j从num[i].s开始到队列中的值+L最终输出dp[k][n]即可
-
#include<iostream> #include<cstdio> #include<algorithm> #include<vector> #include<cstring> using namespace std; #define maxn 16666 #define ll long long int n,k; ll dp[111][maxn]; pair<int,ll>que[maxn]; struct node { int l,p,s; } num[111]; bool cmp(node a,node b) { return a.s<b.s; } int main() { while(~scanf("%d%d",&n,&k)) { for(int i=1; i<=k; i++) scanf("%d%d%d",&num[i].l,&num[i].p,&num[i].s); sort(num+1,num+1+k,cmp); int head,tail; memset(dp,0,sizeof(dp)); for(int i=1; i<=k; i++) { head=1; tail=0; que[++tail]=make_pair(0,0); for(int j=1; j<=n; j++) { dp[i][j]=max(dp[i][j-1],dp[i-1][j]); if(j<num[i].s) { while(head<=tail&&que[tail].second<dp[i-1][j]-j*num[i].p)tail--; que[++tail]=make_pair(j,dp[i-1][j]-j*num[i].p); } else { while(head<=tail&&que[head].first+num[i].l<j)head++; if(head<=tail) dp[i][j]=max(dp[i][j],que[head].second+j*num[i].p); } } } printf("%lld\n",dp[k][n]); } return 0; }
D - Fence POJ - 单调队列优化-DP
猜你喜欢
转载自blog.csdn.net/BePosit/article/details/83187515
今日推荐
周排行