CF8C Looking for Order 题解

题意:平面上有n个点,一开始在原点,每次从原点出发最多经过2个点就必须返回原点,问走完所有点的最小距离,和一种走法。\((n \leq 24)\)

考虑贪心,由于每个点到达原点的路都至少走一次,所以可以把这些边先加进去然后去掉,然后变成在n个点的完全图中选边使其成为二分图且总权值和最小,这时贪心显然没有正确性。

根据数据范围考虑状压dp,暴力转移复杂度不能接受,容易发现对于任意两次从原点出发,取点,返回原点的过程,将它们互换不会影响结果。从而只需要记录哪些点走了哪些点没走。开一个\(2^{24}\)大小的数组即可,另外为了存储路径,还要再开一个相同大小的数组,这样已经几乎卡满空间。

考虑如何保证不反复尝试同一走法的不同顺序,只需要在找到第一个可以转移的单次的两个取点的第一个点即可,这样保证了不会反复转移。

注意输出路径,开一个数组存放一种dp状态是又哪一种转移过来的。(为什么不能反过来存,感觉也有正确性,毕竟是最优子结构)。

接下来是简单转移,枚举i,j:

\[dp_{t|(1<<i-1|1<<j-1)}=dp_t+dis[0][i]+dis[0][j]+dis[i][j] \]

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <assert.h>
using namespace std;
const int N = 25;
const long long INF = 0x7ffffffff;
struct point
{
    long long x, y;
} p[N];
long long getdis(int a, int b)
{
    int x1 = p[a].x, x2 = p[b].x, y1 = p[a].y, y2 = p[b].y;
    return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
}
int n;
int dis[N][N];
long long dp[(1 << N - 1) + 5], route[(1 << N - 1) + 5];
int main()
{
    ios::sync_with_stdio(false);
    cin >> p[0].x >> p[0].y;
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> p[i].x >> p[i].y;
    for (int i = 0; i <= n; i++)
        for (int j = 0; j <= n; j++)
            dis[i][j] = getdis(i, j);
    for (int i = 1; i <= (1 << n) - 1; i++)
        dp[i] = INF;
    dp[0] = 0;
    for (int t = 0; t <= (1 << n) - 1; t++)
    {
        if (dp[t] == INF)
            continue;
        for (int i = 1; i <= n; i++)
        {
            if (t & (1 << i - 1))
                continue;
            for (int j = 1; j <= n; j++)
            {
                if (t & (1 << j - 1))
                    continue;
                if (dp[t] + dis[0][i] + dis[0][j] + dis[i][j] < dp[t | (1 << i - 1) | (1 << j - 1)])
                {
                    dp[t | (1 << i - 1) | (1 << j - 1)] = dp[t] + dis[0][i] + dis[0][j] + dis[i][j];
                    route[t | (1 << i - 1) | (1 << j - 1)] = t;
                }
            }
            break;
        }
    }
    cout << dp[(1 << n) - 1] << endl;
    for (int i = (1 << n) - 1; i; i = route[i])
    {
        cout << 0 << " ";
        int temp = route[i] ^ i;
        for (int j = 1; j <= n; j++)
            if (temp & (1 << j - 1))
                cout << j << " ";
    }
    cout << 0 << endl;
}
上一篇:Java开发笔记XML报文的解析


下一篇:Zookeeper学习(三):选举算法和流程