题意:
有一个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为0则不限制停在哪一点 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 ",Ans[k]); } }