1 条题解
-
0
显而易见,如果每次询问时目的地完全相同,只是询问时的顺序不同,并不会改变结果。且由于询问量巨大,会存在很多重复的情况,所以考虑对所有情况进行预处理。
对于每一个目的地,只会是有订单或没有订单两种情况,则考虑状态压缩DP求解;
每一个状态DP【i】【j】代表订单情况。(将i转化为二进制,1代表该位上对应的目的地在订单中。j为目的地的编号,代表该状态下最后一个节点为j)
不过,需要确定该状态下,哪些目的地在该状态(i)下。
由树状数组的启发,想到可以使用Lowbit函数(结合参考代码理解)求出每一个存在于该状态下的目的地作为该状态下的终点。
于是便可锁定一条边从前一个状态到现在的状态。
例如 对于i=101(5) 运用Lowbit函数,找到了序号1和序号3在该状态中。
则DP【5】【1】代表对于订单情况为订单1+订单3这个状态下把订单序号1为终点的状态
于是可以遍历这个状态的前一个状态DP【4】【j】代表前一个状态为只有订单3在订单序列中。
同样的,采用Lowbit函数,找到该状态下订单3在订单序列中。即可找到一条唯一的路径DP【4】【3】-->DP【5】【1】 ,更新DP【5】【1】=min(DP【5】【1】,DP【4】【3】+dis(1->3))
同理假设i=111(7) 找到序列1,2,3在状态中。
则当求解DP【7】【1】时,遍历前一个状态i=110(6),DP【7】【1】=min(DP【7】【1】,DP【6】【2】+dis(1->2));DP【7】【1】=min(DP【7】【1】,DP【6】【3】+dis(1->3))......
(结合参考代码理解)
最后一步,处理每一中情况的最小值
即可完成预处理。
每当输入一种订单序号,采用为位运算计算该订单序号对应的状态i。直接查询后输出
#include<bits/stdc++.h> using namespace std; double dp[140005][21]; int n,t; int ju[120000]; struct node{ double x,y; }ma[21]; int lowbit(int i){ return i&-i; } double mindis(int i,int j,int dn){ int x=j; double minn=INT_MAX/10; while(x>=1){ int m=lowbit(x); x-=m; int distant=abs(ma[ju[dn]].x-ma[ju[m]].x)+abs(ma[ju[dn]].y-ma[ju[m]].y); minn=min(minn,dp[j][ju[m]]+distant); } return minn; } void solve(int i){ dp[i][ju[i]]=abs(ma[ju[i]].x)+abs(ma[ju[i]].y); } int main(){ cin>>n>>t; ju[1]=1;ju[2]=2;ju[4]=3;ju[8]=4;ju[16]=5;ju[32]=6;ju[64]=7;ju[128]=8; ju[256]=9;ju[512]=10;ju[1024]=11;ju[2048]=12;ju[4096]=13;ju[8192]=14; ju[16384]=15; for(int i=1;i<=n;i++) cin>>ma[i].x>>ma[i].y; int i; for(int i=1;i<(1<<n);i++){ if(i==1||i==2||i==8||i==4||i==16||i==32||i==64||i==128||i==256||i==512||i==1024||i==2048||i==4096||i==8192||i==16384){ solve(i); continue; }; int x=i; while(x>=1){ int m=lowbit(x); x-=m; dp[i][ju[m]]=mindis(i,i-m,m); } } for(int i=1;i<=t;i++){ int dest=0,d; cin>>d; double ans=INT_MAX/10; for(int j=1;j<=d;j++) { int a; cin>>a; dest+=(1<<(a-1)); } for(int j=1;j<=n;j++) if(dp[dest][j]!=0) ans=min(ans,dp[dest][j]); cout<<ans<<" "; } return 0; }
- 1
信息
- ID
- 262
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 9
- 标签
- (无)
- 递交数
- 62
- 已通过
- 5
- 上传者