Description
给出 n n 个物品的二维坐标以及起点坐标,每次要从起点出去拿一件或两件物品回到起点,两点之间距离定义为欧氏距离的平方,问把所有物品拿回起点所走最短距离以及对应最短距离下拿物品的顺序
Input
首先输入两个整数表示起点坐标,之后输入一整数 n n 表示物品数量,最后行每行两个整数 xi,yi x i , y i 表示第 i i 个物品的坐标
Output
把起点编号为 0 0 ,物品编号为~ n n ,输出最短距离和对应的路径
Sample Input
0 0
2
1 1
-1 1
Sample Output
8
0 1 2 0
Solution
把件物品编号 0 0 ~,起点编号为 n n ,表示 i,j i , j 两点距离,用 n n 位表示每个物品的状态, 0 0 表示已经被拿了,表示还没被拿,多次出去拿东西的次序无关,主要是每次出去拿的物品编号,故对于一个状态 S S ,我们默认去拿的二进制表示中编号最小的 1 1 ,以表示对于状态 S S 至少要走多少距离可以把这些物品拿到,假设第位为 S S 中编号最小的,如果只拿一件,那么有转移 dp[S]=min(dp[S],dp[S−2i]+2⋅d(i,n)) d p [ S ] = m i n ( d p [ S ] , d p [ S − 2 i ] + 2 ⋅ d ( i , n ) ) ,如果拿两件,那么找到 S S 二进制表示中另一个所在位置 j j ,那么有转移,最后 dp[2n−1] d p [ 2 n − 1 ] 即为最短距离,拿物品的顺序在转移过程中记录一个状态 S S 是由转移过来的,那么这次拿的物品即为 S−T S − T 中 1 1 <script type="math/tex" id="MathJax-Element-38">1</script>对应的编号
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=(1<<24)+5;
int n,xx,yy,x[25],y[25],dis[25][25],dp[maxn],pre[maxn];
int dfs(int S)
{
if(dp[S]!=-1)return dp[S];
dp[S]=INF;
for(int i=0;i<n;i++)
if((S>>i)&1)
{
int T=S^(1<<i);
int temp=dfs(T);
if(dp[S]>temp+2*dis[i][n])
{
dp[S]=temp+2*dis[i][n];
pre[S]=T;
}
for(int j=i+1;j<n;j++)
if((S>>j)&1)
{
int R=T^(1<<j);
int temp=dfs(R);
if(dp[S]>temp+dis[n][i]+dis[i][j]+dis[j][n])
{
dp[S]=temp+dis[n][i]+dis[i][j]+dis[j][n];
pre[S]=R;
}
}
break;
}
return dp[S];
}
void output(int S)
{
if(!S)return ;
for(int i=0;i<n;i++)
if((S^pre[S])&(1<<i))
{
printf("%d ",i+1);
}
printf("0 ");
output(pre[S]);
}
int main()
{
scanf("%d%d",&xx,&yy);
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d%d",&x[i],&y[i]);
x[n]=xx,y[n]=yy;
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
dis[i][j]=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
int N=1<<n;
memset(dp,-1,sizeof(dp));
dp[0]=0;
printf("%d\n",dfs(N-1));
printf("0 ");
output(N-1);
printf("\n");
return 0;
}