[Cattle] cargo passenger network packet

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];
    }
}

Guess you like

Origin www.cnblogs.com/SillyTieT/p/11785452.html