hdu多校第一场1003 (hdu6580)Milk 背包

题意:

有一个n*m的矩阵,左右可以随便走,但只能在每一行的中点往下走,每走一格花费时间1.

现在这个矩阵里放了k瓶牛奶,第i个牛奶喝下去需要ti时间

起点是(1,1)

对于每个i∈[1,k],问喝掉k瓶牛奶花费的最小时间

题解:

首先离散化行。

记第 i 行的牛奶数为 ci,则对于第 i 行,求出在行内向左/右走喝 1,2,...,ci 包牛奶并 且回到/不回到行中点的最短时间,然后合并背包求出在第 i 行内喝 1,2,...,ci 包牛奶并且 回到/不回到行中点的最短时间,然后和在前 i−1 行喝牛奶并回到中点的背包合并,求出 在前 i 行内喝 1,2,...,k 包牛奶并且回到/不回到行中点的最短时间,并用不回到中点的背包 更新答案。每一行处理的复杂度为 O(kci),因此总复杂度为 O(k2)。

注意对第一行因为是从最左边而不是中间开始,所以要特殊处理。

#include<bits/stdc++.h>
#define MAXN 10004
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
struct Milk{
    int x,y,t;
    friend bool operator >(const Milk &a,const Milk &b){
        return a.y>b.y;
    }
    friend bool operator <(const Milk &a,const Milk &b){
        return a.y<b.y;
    }
    
    Milk(){}
    Milk(int a,int b,int c){
        x=a;y=b;t=c;
    }
}milk[MAXN];
vector<LL> solve(const vector<Milk> &a,int dest){
    //调用引用,减小常数
    //背包算出停在dest点花费最小时间
    //若dest为-1则不限制停在哪一点 
    vector<LL> c(a.size(),INF);
    vector<LL> t(a.size(),INF);
    c[0]=(dest==-1)?0:abs(dest-a[0].y);
    t[0]=0;
    for(int i=1;i<a.size();++i){
        int len=abs(a[i].y-a[i-1].y);
        for(int j=0;j<i;++j)t[j]+=len;
        for(int j=i;j>=1;--j)t[j]=min(t[j],t[j-1]+a[i].t);
        for(int j=0;j<=i;++j){
            LL tmp=t[j]+(dest==-1?0:abs(dest-a[i].y));
            c[j]=min(c[j],tmp);
        }
    }
    return c;
}
vector<LL> Merge(const vector<LL> &a,const vector<LL> &b){
    vector<LL> c(a.size()+b.size()-1,INF);
    for(int i=0;i<a.size();++i){
        for(int j=0;j<b.size();++j){
            c[i+j]=min(c[i+j],a[i]+b[j]);
        }
    }
    //两个参数分别是朝向两个方向喝多少瓶牛奶花费最少时间,把这俩合并 
    return c;
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n,m,k;
        scanf("%d %d %d",&n,&m,&k);
        vector<int> disc;
        disc.push_back(1);
        for(int i=1;i<=k;i++){
            scanf("%d %d %d",&milk[i].x,&milk[i].y,&milk[i].t);
            disc.push_back(milk[i].x);
        }
        sort(disc.begin(),disc.end());
        disc.erase(unique(disc.begin(),disc.end()),disc.end());
        //去重 
        vector<Milk> a[MAXN];
        LL Ans[MAXN];
        for(int i=0;i<disc.size();++i)a[i].clear();
        
        for(int i=1;i<=k;++i){
            int tmp=lower_bound(disc.begin(),disc.end(),milk[i].x)-disc.begin();
            a[tmp].push_back(milk[i]);
            Ans[i]=INF;
        }
        //将行离散化 
        for(int i=0;i<disc.size();++i){
            sort(a[i].begin(),a[i].end());
        } 
        vector<LL> f[2];//存储结果 
        vector<Milk> t=a[0];//存储当前行 
        
        t.insert(t.begin(),Milk(1,1,0));
        
        f[0]=solve(t,(m+1)/2);
        vector<LL> g=solve(t,-1);
        for(int i=0;i<t.size();++i)Ans[i]=min(Ans[i],g[i]);
        
        for(int i=1;i<disc.size();++i) {
            vector<Milk>::iterator sp=lower_bound(a[i].begin(),a[i].end(),Milk(i,(m+1)/2,0));
            vector<Milk> t0(a[i].begin(),sp);
            vector<Milk> t1(sp,a[i].end());
            
            t0.push_back(Milk(i,(m+1)/2,0));
            reverse(t0.begin(),t0.end()); 
            
            t1.insert(t1.begin(),Milk(i,(m+1)/2,0));

            vector<LL> f0,f1,g0,g1;
            f0=solve(t0,(m+1)/2);
            f1=solve(t1,(m+1)/2);
            g0=solve(t0,-1);
            g1=solve(t1,-1);
            //向两个方向分别背包 
            
            g0=Merge(f1,g0);
            g1=Merge(f0,g1);
            

            vector<LL> g(g0.size());
            
            for(int j=0;j<g.size();++j){
                g[j]=min(g0[j],g1[j]);
            }
                
            g=Merge(f[(i-1)&1],g);
            //把每层的背包合并 

            f[i&1]=Merge(f[(i-1)&1],Merge(f0,f1));
            
            for(int j=0;j<g.size();++j) {
                Ans[j]=min(Ans[j],g[j]+=disc[i]-disc[i-1]);
                f[i&1][j]+=disc[i]-disc[i-1];
            }
        }
        
        for(int i=1;i<k;i++){
            printf("%lld ",Ans[i]);
        }
        printf("%lld\n",Ans[k]);
    }
} 

 

posted @ 2019-07-25 21:42  Isakovsky  阅读(675)  评论(2编辑  收藏  举报