题目链接: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;
}