题目链接:http://codeforces.com/contest/8/problem/C
题意:平面上有n个物品,这个小朋友会去拿这些物品,然后拿到返回包的位置。但是这个小朋友一次最多拿两个物品,问你怎么去拿,才能使得把所有物品都拿到包的位置,且走的距离和最小
解法: 比较显然的状压,状压中有一个剪枝,显然拿的顺序是随意的,我先拿和后拿都是一样的。所以可以直接return就好了。这题质量感觉蛮高的。。
//CF 8C 状压DP
#include <bits/stdc++.h>
using namespace std;
const int maxn = 24;
int dp[1<<maxn], pre[1<<maxn];
int n, d[maxn+2][maxn+2], x[maxn+2], y[maxn+2];
int dfs1(int x)
{
if(dp[x]!=-1) return dp[x];
dp[x] = 1e9;
for(int i = 0; i < n; i++){
if(x&(1<<i)){
int nxt = dfs1(x^(1<<i));
if(dp[x] > nxt+d[n][i]+d[i][n]){
dp[x] = nxt+d[n][i]+d[i][n];
pre[x]=x^(1<<i);
}
for(int j = i+1; j < n; j++){
if(x&(1<<j)){
int nxt = dfs1(x^(1<<i)^(1<<j));
if(dp[x]>nxt+d[n][i]+d[i][j]+d[j][n]){
dp[x] = nxt+d[n][i]+d[i][j]+d[j][n];
pre[x]=x^(1<<i)^(1<<j);
}
}
}
break;//这个剪枝超级重要,不加会T
}
}
return dp[x];
}
void dfs2(int x){
if(x){
for(int i = 0; i < n; i++){
if((x^pre[x])&(1<<i)){
printf("%d ",i+1);
}
}
printf("0 ");
dfs2(pre[x]);
}
}
int main()
{
scanf("%d%d", &x[0], &y[0]);
scanf("%d", &n);
x[n] = x[0], y[n] = y[0];
for(int i = 0; i < n; i++){
scanf("%d%d", &x[i], &y[i]);
}
for(int i = 0; i <= n; i++){
for(int j = 0; j <= n; j++){
d[i][j] = (x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
}
}
memset(dp, -1, sizeof(dp));
dp[0] = 0;
int mask = (1<<n)-1;
int ans = dfs1(mask);
printf("%d\n", ans);
printf("0 ");
dfs2(mask);
printf("\n");
return 0;
}