LA3983 Robotruck (单调队列优化DP)
来源:互联网 发布:js求二维数组的最大值 编辑:程序博客网 时间:2024/04/30 15:41
Problem links: https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1984
题目大意:给出一些垃圾点的坐标x,y(x,y为非负整数)以及每个垃圾的重量,给出机器人的携带量cap,垃圾桶在原点(0,0),机器人从原点出发按顺序拣垃圾,在满足不超过cap的情况下,把垃圾带回原点(回到原点后,携带量重新为cap)。问把所有垃圾拣完需要走的最短路。
一开始想到dp[i][res]表示当前到达i点,当前剩余携带量为res时的最优解。此时,决策有两种,一是把原先携带的先带回原点,即x[i-1]+y[i-1],然后再从原点直接出发到i。另一决策可以是,在满足剩余携带量不小于w[i]时,从i-1走到i。
即状态转移为dp[i][res] = min(x[i-1]+y[i-1]+x[i]+y[i]+dp[i+1][cap-w[i]], x[i]-x[i-1]+y[i]-y[i-1]+dp[i+1][res-w[i]]);复杂度为O(n*cap),其中n<=100000,cap<=100,复杂度很大。但是一开始看错了,以为n<=10000,直接写了个记忆化DP,居然过了。。
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;#define SIZE 100010int cas,cap,n;int x[SIZE],y[SIZE],w[SIZE]; int dp[SIZE][110];bool vis[SIZE][110];int Abs(int x){return x>0?x:-x;}int dfs(int cur,int res){if(cur == n+1)return (x[n]+y[n]);if(vis[cur][res])return dp[cur][res];int tem1 = 0, tem2 = 0;tem2 += x[cur-1]+y[cur-1];tem2 += x[cur]+y[cur];tem2 += dfs(cur+1,cap-w[cur]);if(res >= w[cur]){tem1 += Abs(x[cur]-x[cur-1])+Abs(y[cur]-y[cur-1]);tem1 += dfs(cur+1,res-w[cur]);tem2 = min(tem1,tem2);}dp[cur][res] = tem2;vis[cur][res] = true;return dp[cur][res];}int main(){scanf("%d",&cas);while(cas--){scanf("%d%d",&cap,&n);for(int i=1; i<=n; i++)scanf("%d%d%d",&x[i],&y[i],&w[i]);memset(dp,0,sizeof(dp));memset(vis,0,sizeof(vis));dp[1][cap] = x[1]+y[1]+dfs(2,cap-w[1]);printf("%d\n",dp[1][cap]);if(cas)puts("");}return 0;}
正解应该是用单调队列优化,及时删掉肯定不是最优解的点。复杂度降为O(n)
令dp[i]表示拣掉前i个垃圾,并送回原点的最小值。则状态转移方程为:
dp[i] = dp[k] + dis[k+1] + tot[i]-tot[k+1] + dis[i];其中dis[k+1]表示点k+1到原点的直接距离,方程中表示前k个点已经走完,并且机器人在原点,dis[k+1]既是从原点直接走到点k+1,tot[i]表示从点1沿着顺序一直走到点i,即1,2,3...i,故tot[i]-tot[k+1]表示从k+1走到i,最后,再从i走回原点,距离为dis[i]。但是直接枚举k肯定是不行的,这里就用到单调队列来优化。首先,最后结果肯定是dp[n],所以按顺序计算dp[1~n]时,前面肯定是有些子状态是会随着计算后变成没用了。按题意可以知道,当 w[k]+w[k+1]+...w[i] > cap的时候,k就是用不上的了,因为i不可能从k转移而来了。然后,假设有子状态dp[j],dp[k],转移时有dp[i]=dp[j]+dis[j+1]+tot[i]-tot[j+1]+dis[i],以及dp[i] = dp[k]+dis[k+1]+tot[i]-tot[k+1]+dis[i],若dp[i]从dp[j]中转移而来比从dp[k]中转移而来更优(即值更小),则有dp[j]+dis[j+1]+tot[i]-tot[j+1]+dis[i] < dp[k]+dis[k+1]+tot[i]-tot[k+1]+dis[i],化简得dp[j]+dis[j+1]-tot[j+1] < dp[k]+dis[k+1]-tot[k+1],所以可以将最优子子状态存到一个队列里,当当前i不能从队头转移而来时,删掉队头,当决策i比队尾更优时,删掉队尾的那个决策,因为后续状态肯定是从更优的决策i转移而来了。
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;#define SIZE 100010int cas,cap,n;int x[SIZE],y[SIZE],w[SIZE];int dp[SIZE],dis[SIZE],tot[SIZE],sumw[SIZE];int q[SIZE],h,t;int Abs(int x){ return x>0?x:-x;}int main(){ scanf("%d",&cas); while(cas--) { scanf("%d%d",&cap,&n); x[0] = y[0] = sumw[0] = tot[0] = 0; for(int i=1; i<=n; i++) { scanf("%d%d%d",&x[i],&y[i],&w[i]); dis[i] = x[i] + y[i]; tot[i] = tot[i-1] + Abs(x[i]-x[i-1]) + Abs(y[i]-y[i-1]); sumw[i] = sumw[i-1] + w[i]; } dp[0] = 0; h = t = 0; q[++t] = 0; for(int i=1; i<=n; i++) { while(h+2 <= t && sumw[i]-sumw[q[h+1]] > cap) ++h; dp[i] = dp[q[h+1]] + dis[q[h+1]+1] + dis[i] + tot[i] - tot[q[h+1]+1]; while(h+2 <= t && dp[i]+dis[i+1]-tot[i+1] <= dp[q[t]]+dis[q[t]+1]-tot[q[t]+1]) --t; q[++t] = i; } printf("%d\n",dp[n]); if(cas)puts(""); }}
- LA3983 Robotruck (单调队列优化DP)
- LA3983-Robotruck(dp + 单调队列)
- LA 3983 - Robotruck 单调队列优化DP
- uva 1169 - Robotruck (单调队列优化dp)
- UVA 1169 Robotruck 单调队列优化 DP
- uva 1169 - Robotruck(dp+单调队列)
- UVALive - 3983 - Robotruck (DP+单调队列)
- LA3983 Robotruck
- LA3983 - Robotruck
- LA3983 Robotruck
- LA3983 Robotruck
- [LA3983][DP][单调队列]捡垃圾的机器人
- *UVA 1169Robotruck ACM解题报告(dp单调队列优化)
- UVA 1169 DP+单调队列优化 Robotruck 捡垃圾的机器人
- UVA 1169 Robotruck(dp+单调队列滑动窗口)
- 单调队列优化DP
- 单调队列优化DP
- dp单调队列优化
- 130827解题报告
- VC6.0不能添加和打开工程文件的问题
- Chrome下安装Advanced REST Client插件
- ubuntu命令行下中文乱码的解决方案 (我采取了其中方案一与方案二,都还没成功—待定)
- asp.net ToString格式大全与Convert.ToString区别
- LA3983 Robotruck (单调队列优化DP)
- 空指针、NULL指针、零指针
- android获取屏幕尺寸、密度(判断手机屏幕类型)
- Android 监听home键,及软键盘的弹起和关闭
- ubuntu下卸载软件(两种方法)
- go语言接口使用
- J2EE项目开发流程
- android4.0系统旋转270度的方法
- 欢迎报名参加CSDN云计算俱乐部上海“hadoop技术开发与应用实践分享会”