题意:
一条线段上有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]; }View Code