Candy Replenishing Robot

Find the Minimum Number

直接模拟

Melodious password

dfs输出方案

Poles

题意:有多个仓库,只能从后面的仓库运动前面的仓库,现在要把仓库合并成k个,移动一个仓库i到另个仓库j的代价是costi*(xi-xj),问最小代价。

解一下就会发现是个斜率优化,做k次就可以了

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 100101
#define LL long long
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define dow(i,l,r) for(int i=r;i>=l;i--)
using namespace std;

LL sumd[maxn],sumw[maxn],f[2][maxn],d[maxn],w[maxn];
int n,m,p[maxn];

LL calc(int x,int y)
{
    return f[y][x]-sumd[x];
}
int main()
{
    scanf("%d %d",&n,&m);
    dow(i,1,n) scanf("%lld %lld",&d[i],&w[i]);
    rep(i,1,n) {
        sumw[i]=sumw[i-1]+w[i];
        sumd[i]=sumd[i-1]+d[i]*w[i];
    }
//    rep(i,1,n) printf("%d %lld %lld %lld\n",i,d[i],sumw[i],sumd[i]);printf("\n");
    int now=0;
    rep(i,1,n) f[0][i]=-(sumw[i]-sumw[0])*d[i]+sumd[i]-sumd[0];
//    rep(i,1,n) printf("%lld ",f[0][i]);printf("\n");
    rep(i,2,m) {
        now=1-now;
        rep(j,1,n) f[now][j]=0;
        int head,tail;
        head=tail=1;
        p[1]=i-1;
        rep(j,i,n) {
    //        printf("%d %d\n",head,tail);
    //        rep(k,head,tail) printf("%d ",p[k]);printf("\n");
            while (head<tail && 
                d[j]*(sumw[p[head+1]]-sumw[p[head]])<calc(p[head],1-now)-calc(p[head+1],1-now)) 
                    ++head;
            f[now][j]=(sumw[p[head]]-sumw[j])*d[j]+(sumd[j]-sumd[p[head]])+f[1-now][p[head]];
        //    printf("%lld %lld\n",calc(p[tail],1-now),calc(j,1-now));
            while (head<tail && 
                (calc(p[tail],1-now)-calc(j,1-now))*(sumw[p[tail]]-sumw[p[tail-1]])>(calc(p[tail-1],1-now)-calc(p[tail],1-now))*(sumw[j]-sumw[p[tail]]))
                    --tail;
            p[++tail]=j;
        }
    //    rep(j,1,n) printf("%lld ",f[now][j]);printf("\n");
    }
    printf("%lld\n",f[now][n]);
    return 0;
} 
View Code

相关文章: