Description
给出nn个物品的二维坐标以及起点坐标,每次要从起点出去拿一件或两件物品回到起点,两点之间距离定义为欧氏距离的平方,问把所有物品拿回起点所走最短距离以及对应最短距离下拿物品的顺序
Input
首先输入两个整数表示起点坐标,之后输入一整数nn表示物品数量,最后行每行两个整数xi,yixi,yi表示第ii个物品的坐标
Output
把起点编号为00,物品编号为~nn,输出最短距离和对应的路径
Sample Input
0 0
2
1 1
-1 1
Sample Output
8
0 1 2 0
Solution
把件物品编号00~,起点编号为nn,表示i,ji,j两点距离,用nn位表示每个物品的状态,00表示已经被拿了,表示还没被拿,多次出去拿东西的次序无关,主要是每次出去拿的物品编号,故对于一个状态SS,我们默认去拿的二进制表示中编号最小的11,以表示对于状态SS至少要走多少距离可以把这些物品拿到,假设第位为SS中编号最小的,如果只拿一件,那么有转移dp[S]=min(dp[S],dp[S−2i]+2⋅d(i,n))dp[S]=min(dp[S],dp[S−2i]+2⋅d(i,n)),如果拿两件,那么找到SS二进制表示中另一个所在位置jj,那么有转移,最后dp[2n−1]dp[2n−1]即为最短距离,拿物品的顺序在转移过程中记录一个状态SS是由转移过来的,那么这次拿的物品即为S−TS−T中11对应的编号
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;
}