1 条题解

  • 0
    @ 2024-11-27 14:02:26

    显而易见,如果每次询问时目的地完全相同,只是询问时的顺序不同,并不会改变结果。且由于询问量巨大,会存在很多重复的情况,所以考虑对所有情况进行预处理。

    对于每一个目的地,只会是有订单或没有订单两种情况,则考虑状态压缩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
    上传者