链接:https://ac.nowcoder.com/acm/contest/4462/D
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
我们把房间按照笛卡尔坐标系进行建模之后,每个点就有了一个坐标。
假设现在房子里有些纸片需要被收集,收集完纸片你还要回归到原来的位置,你需要制定一个策略来使得自己行走的距离最短。
你只能沿着 x 轴或 y 轴方向移动,从位置 (i,j) 移动到相邻位置 (i+1,j),(i-1,j),(i,j+1) 或 (i,j-1) 距离增加 1。
输入描述:
在第一行中给出一个T,1≤T≤10, 代表测试数据的组数。
对于每组输入,在第一行中给出房间大小,第二行给出你的初始位置。
接下来给出一个正整数 n,1≤n≤10 代表纸片的个数。
接下来 n 行,每行一个坐标代表纸片的位置。
保证房间小于 20×20 ,纸片一定位于房间内。
输出描述:
对于每组输入,在一行中输出答案。
格式参见样例。
输入
1 10 10 1 1 4 2 3 5 5 9 4 6 5
输出
The shortest path has length 24
一、全排列暴力枚举
因为最多只有10片纸片,所以大可枚举所有的收集顺序,然后在其中取一个最小值。
我们可以用求1~k的全排列,计算所有可能的走法,由于纸片数不超过10,所以走法最多只有10!种。
直接全排列计算即可,用DFS或next_permutation均可。
DFS求全排列:
1 #include <bits/stdc++.h> 2 typedef long long LL; 3 const int INF=0x3f3f3f3f; 4 const double eps =1e-8; 5 const int mod=1e9+7; 6 const int maxn=1e5+10; 7 using namespace std; 8 9 int n,m,k; 10 int stx,sty; 11 pair<int,int> pos[15]; 12 int vis[15]; 13 int ans; 14 15 void DFS(int x,int y,int num,int step) 16 { 17 if(num==0) 18 { 19 step+=abs(x-stx)+abs(y-sty); 20 if(step < ans) ans=step; 21 return ; 22 } 23 for(int i=1;i<=k;i++) 24 { 25 if(vis[i]==0) 26 { 27 vis[i]=1; 28 int xx=pos[i].first, yy=pos[i].second; 29 DFS(xx,yy,num-1,step+abs(xx-x)+abs(yy-y)); 30 vis[i]=0; 31 } 32 } 33 } 34 35 36 int main() 37 { 38 #ifdef DEBUG 39 freopen("sample.txt","r",stdin); 40 #endif 41 42 int T; 43 scanf("%d",&T); 44 while(T--) 45 { 46 memset(vis,0,sizeof(vis)); 47 ans=INF; 48 scanf("%d %d %d %d %d",&n,&m,&stx,&sty,&k); 49 for(int i=1;i<=k;i++) 50 { 51 int x,y; 52 scanf("%d %d",&x,&y); 53 pos[i]={x,y}; 54 } 55 DFS(stx,sty,k,0); 56 printf("The shortest path has length %d ",ans); 57 } 58 59 return 0; 60 }
next_permutation求全排列
1 #include <bits/stdc++.h> 2 typedef long long LL; 3 const int INF=0x3f3f3f3f; 4 const double eps =1e-8; 5 const int mod=1e9+7; 6 const int maxn=1e5+10; 7 using namespace std; 8 9 pair<int,int> pos[15]; 10 int rk[15]; 11 12 int main() 13 { 14 #ifdef DEBUG 15 freopen("sample.txt","r",stdin); 16 #endif 17 18 int T; 19 scanf("%d",&T); 20 while(T--) 21 { 22 int n,m,k,stx,sty; 23 int ans=INF; 24 scanf("%d %d %d %d %d",&n,&m,&stx,&sty,&k); 25 for(int i=1;i<=k;i++) 26 { 27 int x,y; 28 scanf("%d %d",&x,&y); 29 pos[i]={x,y}; rk[i]=i; 30 } 31 do 32 { 33 int sum=0, prex=stx, prey=sty; 34 for(int i=1;i<=k;i++) 35 { 36 int x=pos[rk[i]].first, y=pos[rk[i]].second; 37 sum+=abs(x-prex)+abs(y-prey); 38 prex=x; prey=y; 39 } 40 ans=min(ans,sum+abs(prex-stx)+abs(prey-sty)); 41 }while(next_permutation(rk+1,rk+1+k)); 42 printf("The shortest path has length %d ",ans); 43 } 44 45 return 0; 46 }
二、状压dp
把每个纸片都不重不漏地恰好经过一次,很容易想到旅行商问题,即TSP问题(每个城市只能拜访一次,而且最后要回到原来出发的城市。),可以用状压dp。
借助二进制数来进行DP。
1 #include <bits/stdc++.h> 2 typedef long long LL; 3 const int INF=0x3f3f3f3f; 4 const double eps =1e-8; 5 const int mod=1e8; 6 const int maxn=1e5+10; 7 using namespace std; 8 9 pair<int,int> pos[15]; 10 int dp[1<<11][11];//dp[i][j]目前走过的状态是二进制i,且最后一个走过的点是j 11 12 int cal(int a,int b) 13 { 14 return abs(pos[b].first-pos[a].first)+abs(pos[b].second-pos[a].second); 15 } 16 17 int main() 18 { 19 #ifdef DEBUG 20 freopen("sample.txt","r",stdin); 21 #endif 22 23 int T; 24 scanf("%d",&T); 25 while(T--) 26 { 27 int n,m,k; 28 scanf("%d %d %d %d %d",&n,&m,&pos[0].first,&pos[0].second,&k);//pos[0]是初始位置 29 for(int i=1;i<=k;i++) 30 { 31 int x,y; 32 scanf("%d %d",&x,&y); 33 pos[i]=make_pair(x,y); 34 } 35 int ans=INF; 36 memset(dp,INF,sizeof(dp)); 37 for(int i=1;i<=k;i++) 38 dp[1<<(i-1)][i]=cal(0,i); 39 for(int s=0;s<(1<<k);s++) 40 { 41 for(int i=1;i<=k;i++) //遍历该状态的一个点i作为最后一个经过的点 42 { 43 if(s & (1<<(i-1))) 44 { 45 for(int j=1;j<=k;j++)//遍历该状态的一个点j作为从j走到i 46 { 47 if(s & (1<<(j-1))) 48 dp[s][i]=min(dp[s][i],dp[s^(1<<(i-1))][j]+cal(i,j)); 49 } 50 } 51 } 52 } 53 for(int i=1;i<=k;i++) 54 ans=min(ans,dp[(1<<k)-1][i]+cal(i,0)); 55 printf("The shortest path has length %d ",ans); 56 } 57 58 return 0; 59 }
状压DP讲解:https://www.cnblogs.com/Tony-Double-Sky/p/9283254.html
TSP问题DP详解:https://blog.csdn.net/qq_39559641/article/details/101209534
关于TSP的一些题:https://blog.csdn.net/yjr3426619/article/details/83387962
-