大意: $n*m$棋盘, 初始位置$(1,1)$, 横坐标为$\frac{m+1}{2}$时可以向下走, 否则只能左右走, 每走一步花费$1$秒. 有$k$管奶, 第$i$罐位置$(r_i,c_i)$, 要花费$t_i$的时间去喝. 对于所有的$1\le i\le k$, 求出喝完$i$管奶最短用时.
实现略复杂的$dp$题, 按照官方题解写了.
主要思路是对每一行求出向左/向右喝$x$罐奶的最少用时, 然后$dp$合并答案.
#include <iostream> #include <algorithm> #include <cstdio> #include <queue> #define REP(i,a,n) for(int i=a;i<=n;++i) #define PER(i,a,n) for(int i=n;i>=a;--i) #define x first #define y second #define pb push_back using namespace std; typedef long long ll; typedef pair<int,int> pii; const ll INF = 1e15; const int N = 1e4+10; int n, m, k, b[N]; struct {int x,y,t;} a[N]; ll ans[N]; vector<pii> v[N]; vector<ll> solve(vector<pii> a, int s, int x) { a.insert(a.begin(),pii(s,0)); vector<ll> ret(a.size(),INF), t(ret); ret[0] = x==-1?0:abs(x-s); t[0] = 0; for (int i=1; i<a.size(); ++i) { int len = abs(a[i].x-a[i-1].x); REP(j,0,i-1) t[j]+=len; PER(j,1,i) t[j]=min(t[j],t[j-1]+a[i].y); REP(j,1,i) ret[j]=min(ret[j],t[j]+(x==-1?0:abs(x-a[i].x))); } return ret; } vector<ll> Merge(const vector<ll> &L, const vector<ll> &R) { vector<ll> ret(L.size()+R.size()-1,INF); for (int i=0;i<L.size();++i) { for (int j=0;j<R.size();++j) { ret[i+j] = min(ret[i+j], L[i]+R[j]); } } return ret; } void work() { scanf("%d%d%d", &n, &m, &k); *b = 0; REP(i,1,k) { scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].t); b[++*b] = a[i].x; } b[++*b] = 1; sort(b+1,b+1+*b),*b=unique(b+1,b+1+*b)-b-1; REP(i,1,*b) v[i].clear(); REP(i,1,k) { a[i].x=lower_bound(b+1,b+1+*b,a[i].x)-b; v[a[i].x].pb(pii(a[i].y,a[i].t)); ans[i] = INF; } REP(i,1,*b) sort(begin(v[i]),end(v[i])); auto g = solve(v[1],1,-1); for (int i=0;i<g.size();++i) { ans[i] = min(ans[i], g[i]); } int cur = 0; vector<ll> f[2]; f[0] = solve(v[1],1,(m+1)/2); REP(i,2,*b) { cur ^= 1; auto p = lower_bound(begin(v[i]),end(v[i]),pii((m+1)/2,0)); vector<pii> L(begin(v[i]),p), R(p,end(v[i])); reverse(begin(L),end(L)); auto f0 = solve(L,(m+1)/2,(m+1)/2), f1 = solve(R,(m+1)/2,(m+1)/2); auto g0 = solve(L,(m+1)/2,-1), g1 = solve(R,(m+1)/2,-1); g0 = Merge(f1,g0), g1 = Merge(f0,g1); auto g = g0; for (int j=0;j<g.size();++j) { g[j] = min(g[j], g1[j]); } g = Merge(f[!cur],g); f[cur] = Merge(f[!cur],Merge(f0,f1)); for (int j=0;j<g.size();++j) { ans[j] = min(ans[j], g[j]+b[i]-b[i-1]); f[cur][j] += b[i]-b[i-1]; } } REP(i,1,k) { if (i==k) printf("%lld\n",ans[i]); else printf("%lld ",ans[i]); } } int main() { int t; scanf("%d", &t); while (t--) work(); }