题意:给出n个物品的二维坐标以及起点坐标,每次要从起点出去拿一件或两件物品回到起点,两点之间距离定义为欧氏距离的平方,问把所有物品拿回起点所走最短距离以及对应最短距离下拿物品的顺序。
题解:数据量相当小,就透露着一股爆搜或状压的味道。
显然,每个物品取或不取可用一位01来表示,并且对于每一轮来讲取物品的顺序是无关的,所以不妨设每次都取位数最低的物品。
取两件同理,在确定完一件后在用同样的方法确定第二件即可
转移方程
取一件:dp[S]=min(dp[S],dp[S−2i]+2⋅d(i,n))
取两件:dp[S]=min(dp[S],dp[S−2i−2j]+d(n,i)+d(i,j)+d(j,n))
因为还要输出路径,所以用pre记录一下转移方式
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
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;
}