补给【蓝桥杯 第十一届】【决赛】【A组】(压装DP+最短路+dp顺序和平行更新的处理)
作者:互联网
思路:
- n等于20,明显的提醒是要用到压装DP,
- 要问从原点出发,每一个点都要至少走一次的最小距离
- 压装可以看到由哪些点走过了,但是这个时候,我们还需要知道目前是那一个点
- 于是就增加一个元素,二维dp,
- 通过这个二维表示所有情况,进行更行.
- 转移的时候就是:与他连接的边的子情况进行转移,
- 但是有些情况 他跑过去了,还要跑回来着该怎么办呢?,就然他的儿子情况不表,不弄压装子节点,
- 更新是有顺序的,对于子情况无所谓,因为都是已经更行过的了,但是不是子情况,需要s的平行转移,有一个更行顺序,不能有效的把那个情况更行出来,
- 那就在平行倒着更新一遍,这要就能保证了 , 这2句话看待吧,比较好理解
#include <bits/stdc++.h> using namespace std; #define ri register int #define M 21 int n,m; double dp[M][1<<21]; struct dian{ int x,y; }pp[M]; struct node{ int to; double val; }; vector <node> p[M]; double get(int a,int b) { return sqrt((pp[a].x-pp[b].x)*(pp[a].x-pp[b].x)+(pp[a].y-pp[b].y)*(pp[a].y-pp[b].y)); } double dis[M]; struct cmp{ bool operator ()(const int a,const int b)const { return dis[a]>dis[b]; } }; int vis[M]; void dj(int a) { for(ri i=1;i<=n;i++) dis[i]=1e9; dis[1]=0; priority_queue<int,vector<int>,cmp> q; q.push(1); while(!q.empty()) { while(!q.empty()&&vis[q.top()]) q.pop(); if(q.empty()) break; int a=q.top();q.pop(); vis[a]=1; for(ri i=0;i<p[a].size();i++) { int b=p[a][i].to; if(dis[b]>dis[a]+p[a][i].val) { dis[b]=dis[a]+p[a][i].val; q.push(b); } } } } int main(){ ios::sync_with_stdio(false); cin.tie(0); cin>>n>>m; for(ri i=1;i<=n;i++) { cin>>pp[i].x>>pp[i].y; } for(ri i=1;i<=n;i++) { for(ri j=i+1;j<=n;j++) { if(get(i,j)<=m) { node t; t.to=j;t.val=get(i,j); p[i].push_back(t); t.to=i; p[j].push_back(t); } } } for(ri i=1;i<(1<<n);i++) { for(ri j=1;j<=n;j++) { dp[j][i]=1e9; } } dp[1][1]=0; for(ri i=1;i<(1<<n);i++) { for(ri j=1;j<=n;j++) { if((i&(1<<(j-1)))==0) continue; for(ri k=0;k<p[j].size();k++) { int b=p[j][k].to; if((i&(1<<(b-1)))==0) continue; dp[j][i]=min(dp[j][i],dp[b][i^((1<<(j-1)))]+p[j][k].val); dp[j][i]=min(dp[j][i],dp[b][i]+p[j][k].val); } } for(ri j=n;j>=1;j--) { if((i&(1<<(j-1)))==0) continue; for(ri k=0;k<p[j].size();k++) { int b=p[j][k].to; if((i&(1<<(b-1)))==0) continue; dp[j][i]=min(dp[j][i],dp[b][i^((1<<(j-1)))]+p[j][k].val); dp[j][i]=min(dp[j][i],dp[b][i]+p[j][k].val); } } } dj(1); double ans=1e9; for(ri i=1;i<=n;i++) { //cout<<dp[i][(1<<n)-1]+dis[i]<<endl; ans=min(ans,dp[i][(1<<n)-1]+dis[i]); } cout<<fixed<<setprecision(2)<<ans; }View Code
后记:
- 用压装dp时,一定要看这个压装状态是不是合理的,本题要判断 i 节点和 j 节点都要在那个状态内很重要!!!!!
- 对于DP顺序对相同状态下的转移一定要特别注意.
标签:pp,压装,int,蓝桥,ri,dp,dis 来源: https://www.cnblogs.com/Lamboofhome/p/16375479.html