这个题目我最初的做法沿用树形DP的做法,设置一个 dp[i][0]表示机器人在i点不回去的最短路径,dp[i][1]表示机器人在i点回去的最短路径,规划方向为i-1向i转移,结果发现这个不能用树形的结构去描述,当前状态不能仅仅靠前一个状态就决定好了,对于后面的点来说,可能当前点走另外一条路会好一些
故,最后还是使用了书上的结构,其实也很简单,用d[i],表示收完从1-i所有的垃圾并送回原点的垃圾桶的最优距离,显然,最终结果就是d[n];
则,如果某个j点满足 w(j+1,i)<=C,则d[i]=min(d[i],d[j]+dist(j+1,i)+origin(j+1)+origin(i));
origin(i)表示原点到i点的距离,dist(i,j)表示从i点到j点的距离,w表示从某点到另一点一起的总重量。
当然,如果直接从1开始枚举到i来确定j会超时,大白书上用维护一个队列来优化,我没怎么看懂
用的土方法,由于w(j+1)<=C,才能进行状态转移,不妨直接设置一个变量cur保存左边界,每次从cur开始枚举,而且每次都把cur边界往右移(如果当前不满足w<=c的条件,则右移,显然,右移不仅不会影响后续决策,还能缩短时间,因为当前不满足w的条件,后续就更加不会满足)就能避免大量运算。
至于计算距离,还是用老方法,前缀和,来瞬间得到某两点的距离差。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int d[100010]; int total_d[100010],total_w[100010],origins[100010]; int x[100010],y[100010]; int n,M; int func(int x) { return d[x]-total_d[x+1]+origins[x+1]; } int main() { int t; scanf("%d",&t); while (t--) { scanf("%d%d",&M,&n); int w; total_d[0]=total_w[0]=0; x[0]=y[0]=0; for (int i=1;i<=n;i++) { scanf("%d%d%d",&x[i],&y[i],&w); total_d[i]=total_d[i-1]+abs(x[i]-x[i-1])+abs(y[i]-y[i-1]); total_w[i]=total_w[i-1]+w; origins[i]=x[i]+y[i]; d[i]=total_d[i]+origins[i]; } int cur=0; for (int i=1;i<=n;i++) { while (cur<i && total_w[i]-total_w[cur]>M) cur++; int temp=1<<30; for (int j=cur;j<i;j++) { temp=min(temp,func(j)); } if (temp<(1<<30)) d[i]+=temp; } printf("%d ",d[n]); if (t>0) printf(" "); } return 0; }