dp + decision-monotonic
part1.30pts
Obviously, the former is a good 30 minutes to write.
\ (DP [i] [j] \) represents the i-th former boxes divided into j groups of minimum cost.
Complexity: \ (O (n-^. 3) \)
Code:
namespace p30{
ll dp[510][510],ans=2e18+7;
void work(){
memset(dp,63,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
int mx=A[i],mi=A[i];
for(int k=i-1;k>=0;k--){
if(sum[i]-sum[k]>W)break;
dp[i][j]=min(dp[i][j],0LL+dp[k][j-1]+1LL*(sum[i]-sum[k])*j+mx-mi);
mx=max(mx,A[k]);
mi=min(mi,A[k]);
}
}
}
for(int i=1;i<=n;i++)ans=min(ans,dp[n][i]);
cout<<ans;
}
}
part2.60pts
For this part of the cost:
For the i boxes, the total weight of the goods loaded if there is w, then the cost of i * w.
We transformation, assuming that there are five items, numbered 1,2,3,4,5. Wherein the box is loaded {1,2}, {3}, {4,5}.
So these costs is: \ (A [. 1] \ CDOT. 1 + A [2] \ CDOT. 1 + A [. 3] \ cdot2 + A [. 4] \ cdot3 + A [. 5] \ cdot3 \) , may be written as \ ((A [1] + A [2] + A [3] + A [4] + A [5]) + (A [3] + A [4] + A [5]) + (A [4] A + [. 5]) \) .
Thus, when we consider that the i-th item, regardless of the i + 1-th to n-th how to take, at least the final answer take an additional \ (\ sum_ {j = i + 1} ^ {j \ le n} A [J] \) .
So, we set \ (dp [i] \) represents the i-th before considering items end up spending a minimum total cost.
Transfer equation:
\ [DP [0] = \ sum_ ^ {K = {K}. 1 \} n-Le A [K] \]
\[ dp[i]=min\{dp[j]+max(j+1,i)-min(j+1,i)\}+\sum_{k=i+1}^{k\le n}A[k] j\in[0,i-1],\sum_{k=j+1}^{k\le i}A[k]\le W \]
Wherein \ (max (j + 1, i) \) represents the maximum value of j + 1 into i in A, \ (min (j + 1, i) \) indicates the minimum value.
Complexity: \ (O (^ n-2) \)
Code:
namespace p60{
ll dp[MAXN],ans=2e18+7;
void work(){
memset(dp,63,sizeof(dp));
dp[0]=sum[n];
for(int i=1;i<=n;i++){
int mx=A[i],mi=A[i];
for(int j=i-1;j>=0;j--){
if(sum[i]-sum[j]>W)break;
dp[i]=min(dp[i],0LL+dp[j]+mx-mi+sum[n]-sum[i]);
mx=max(mx,A[j]);
mi=min(mi,A[j]);
}
}
cout<<dp[n];
}
}
part3.100pts
For \ (max (j + 1, i) \) and \ (min (j + 1, i) \) may be monotonously stack maintenance.
Since \ (max (j + 1, i) \) and \ (min (j + 1, i) \) is constantly changing, so we need to open a segment tree maintenance.
Complexity: \ (O (n-\ CDOT logN) \)
See details of the code.
Code:
namespace p100 {
int stkMx[MAXN],stkMi[MAXN],topMx,topMi;
struct sgt {
int L,R;
ll sum,lazy;
} tree[MAXN<<2];
ll dp[MAXN];
void Down(int p) {
if(tree[p].lazy==0)return;
tree[p<<1].sum+=tree[p].lazy,tree[p<<1|1].sum+=tree[p].lazy;
tree[p<<1].lazy+=tree[p].lazy,tree[p<<1|1].lazy+=tree[p].lazy;
tree[p].lazy=0;
return;
}
void Up(int p) {
tree[p].sum=min(tree[p<<1].sum,tree[p<<1|1].sum);
}
void build(int L,int R,int p) {
tree[p].L=L,tree[p].R=R;
if(L==R) {
tree[p].sum=2e18+7;
return;
}
int mid=(L+R)>>1;
build(L,mid,p<<1);
build(mid+1,R,p<<1|1);
Up(p);
}
void update1(int pos,int p,ll x) {
if(tree[p].L==tree[p].R) {
tree[p].sum=x;
return;
}
Down(p);
int mid=(tree[p].L+tree[p].R)>>1;
if(pos<=mid)update1(pos,p<<1,x);
else update1(pos,p<<1|1,x);
Up(p);
}
void update2(int L,int R,int p,int x) {
if(tree[p].L==L&&tree[p].R==R) {
tree[p].sum+=x;
tree[p].lazy+=x;
return;
}
Down(p);
int mid=(tree[p].L+tree[p].R)>>1;
if(R<=mid)update2(L,R,p<<1,x);
else if(L>=mid+1)update2(L,R,p<<1|1,x);
else update2(L,mid,p<<1,x),update2(mid+1,R,p<<1|1,x);
Up(p);
}
ll Query(int L,int R,int p) {
if(tree[p].L==L&&tree[p].R==R)return tree[p].sum;
Down(p);
int mid=(tree[p].L+tree[p].R)>>1;
if(R<=mid)return Query(L,R,p<<1);
else if(L>=mid+1)return Query(L,R,p<<1|1);
else return min(Query(L,mid,p<<1),Query(mid+1,R,p<<1|1));
}
void work() {
build(1,n+1,1);
dp[0]=sum[n];
update1(1,1,dp[0]);//我这里是有便宜的,线段树上[i,i]对应的是dp[i-1]+mx-mi
for(int i=1; i<=n; i++) {
while(topMi&&A[i]<A[stkMi[topMi]]) {
int now=stkMi[topMi],la=stkMi[topMi-1];
update2(la+1,now,1,A[stkMi[topMi]]);//把原先减掉的mi给加回来(回溯)
topMi--;
}
update2(stkMi[topMi]+1,i,1,-A[i]);//把现在的mi给减过去(更新)
stkMi[++topMi]=i;
while(topMx&&A[i]>A[stkMx[topMx]]) {
int now=stkMx[topMx],la=stkMx[topMx-1];
update2(la+1,now,1,-A[stkMx[topMx]]);//把原先加上的mx给减掉(回溯)
topMx--;
}
update2(stkMx[topMx]+1,i,1,A[i]);//把现在的mx给加过去(更新)
stkMx[++topMx]=i;
int l=0,r=i-1,res;
while(l<=r){//二分找能转移到的最左端,即满足res到i这一段和刚好小于等于W
int mid=(l+r)>>1;
if(sum[i]-sum[mid]<=W)res=mid,r=mid-1;
else l=mid+1;
}
dp[i]=0LL+Query(res+1,i,1)+sum[n]-sum[i];
update1(i+1,1,dp[i]);
}
cout<<dp[n];
}
}