题意:
一条线段上有n张骨牌(n=1e7), 相邻骨牌距离为1,每张骨牌有其高度和推倒的花费,问最少的花费推倒所有的骨牌。
题解:
- 首先用单调栈维护每个位置往左(右)推能推倒的最远的骨牌
- dp[i]表示1-i倒下的最小花费
- 转移显然只有两种 一种是第i张往左推动
- 另一种是找到往右推动能推倒i的最小花费的地方进行转移 这里可以用单调栈维护最小值

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e7+1000;
ll n,m,siz[N],h[N],L[N],R[N],c[N],dp[N];
vector<ll>H[N],C[N];
int s[N],r;
int main() {
cin>>n>>m;
for(int i=1;i<=n;i++) {
scanf("%lld",&siz[i]);
H[i].resize(siz[i]);
C[i].resize(siz[i]);
for(int j=0;j<siz[i];j++) scanf("%lld",&H[i][j]);
for(int j=0;j<siz[i];j++) scanf("%lld",&C[i][j]);
}
int Q;cin>>Q;
int cnt=0;
while(Q--) {
int id,mul;scanf("%d%d",&id,&mul);
for(int j=0;j<siz[id];j++) h[++cnt]=H[id][j],c[cnt]=C[id][j]*mul;
}
for(int i=1;i<=m;i++) {
while(r&&h[s[r]]+s[r]<=i)
R[s[r--]]=i-1;
s[++r]=i;
}
while(r) R[s[r--]]=m;
for(int i=m;i;i--) {
while(r&&s[r]-h[s[r]]>=i)
L[s[r--]]=i+1;
s[++r]=i;
}
while(r) L[s[r--]]=1;
for(int i=1;i<=m;i++) {
dp[i]=c[i]+dp[L[i]-1];
while(r&&R[s[r]]<i) r--;
if(r) dp[i]=min(dp[i],c[s[r]]+dp[s[r]-1]);
ll cost=c[i]+dp[i-1];
if(r&&cost<c[s[r]]+dp[s[r]-1]||!r)
s[++r]=i;
}
cout<<dp[m];
}
来源:https://www.cnblogs.com/bxd123/p/12234275.html
