[poj 1821] Fence {單調佇列優化dp}
阿新 • • 發佈:2018-12-08
題目
http://poj.org/problem?id=1821
解題思路
先把所有工匠按照
排序,這樣,每個工匠粉刷的木板一定在上一個工匠粉刷的木板之後。
設
表示安排前
個工匠粉刷前
塊木板,工匠們能獲得的最多報酬:
- ,其中
然後我們可以用單調佇列維護一個決策點 單調遞增,數值 單調遞減的佇列。支援如下幾種操作:
- 當 變大時,檢查隊頭元素,把小於 的決策出隊。
- 需要查詢最有決策時,隊頭即為所求。
- 有一個新的決策要加入候選集合時,在隊尾檢查 的單調性,把無用決策從隊尾直接出隊,最後把新決策加入佇列。
程式碼
#include<cstdio>
#include<algorithm>
using namespace std;
struct rec{int L,P,S;}a[110];
int n,m;
int f[110][16010],q[16010];
bool operator <(rec a,rec b){return a.S<b.S;}
int calc(int i,int k){return f[i-1][k]-a[i].P*k;}
int main()
{
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);
for (int i=1;i<=m;i++){
int l=1,r=0; //初始化單調佇列
for (int k=max(0,a[i].S-a[i].L);k<=a[i].S-1;k++){
while (l<=r&&calc(i,q[r])<=calc(i,k)) r--; //插入新決策,維護隊尾單調性
q[++r]=k;
}
for (int j=1;j<=n;j++){
f[i][j]=max(f[i-1][j],f[i][j-1]); //不粉刷時的轉移
if (j>=a[i].S) {
while (l<=r&&q[l]<j-a[i].L) l++; //排除隊頭不合法決策
if (l<=r) f[i][j]=max(f[i][j],calc(i,q[l])+a[i].P*j); //佇列非空時,取隊頭進行狀態轉移
}
}
}
printf("%d",f[m][n]);
}