一个经典的货郎担问题,加上17的范围限制各种明示状态压缩,用二进制来表示走过哪几个城市,我们可以开一个二维数组dp[i][j],表示走过了i的城市,最后在j城市停留的距离。预处理一遍每个城市间的距离,算出dis[i][j],到dp[i][j]的状态可以由[1 ^ (1 << j)][k]转移过来,比较距离保存最优值就行,然后就有转移方程:
dp[i][j] = min(dp[i][j], dp[i ^ (1 << j)][k] + dis[k][j]);
然后我是先处理一下把起点都交换成了第一个点,那时觉得这样好想一点(?),现在看下来有点没必要,差不多其实
剩下的代码说话吧,AC代码:
#include<bits/stdc++.h>
#define FOR(a, b, c) for(int a=b; a<=c; a++)
#define maxn 20
#define maxm 55
#define hrdg 1000000007
#define inf 2147483647
#define llinf 9223372036854775807
#define ll long long
#define pi acos(-1.0)
#define ls p<<1
#define rs p<<1|1
using namespace std;
int n, start;
ll x[maxn], y[maxn];
ll dis[maxn][maxn];
ll dp[1 << maxn][maxn];
int lim[maxn];
inline int read(){
char c=getchar();long long x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
ll get_dis(int i, int j){return llabs(x[i] - x[j]) + llabs(y[i] - y[j]);} //两点间距
int main()
{
n = read();
start = read();
memset(dp, 0x3f, sizeof(dp));
FOR(i, 0, n - 1)
{
x[i] = read();
y[i] = read();
}
swap(x[start - 1], x[0]);
swap(y[start - 1], y[0]); //交换了一下点位
FOR(i, 0, n - 2)
FOR(j, i+1, n - 1)
{
int d = get_dis(i, j);
dis[i][j] = d;
dis[j][i] = d;
}
dp[1][0] = 0;
for (int i = 1; i < (1 << n); i += 2)
{
for (int j = 0; j < n; j++)
{
if(!((i >> j) & 1))
continue;
for (int k = 0; k < n; k++)
{
if (j == k)
continue;
if (!(i >> k & 1)) //从上一次状态没有走过k的话不可能从k到j
continue;
dp[i][j] = min(dp[i][j], dp[i ^ (1 << j)][k] + dis[k][j]);
}
}
}
//FOR(i, 0, n-1) printf("%lld ", dp[(1<<n)-1][i]); cout<<endl;
ll ans = llinf;
for (int i = 0; i < n; i++)
ans = min(ans, dp[(1 << n) - 1][i]);
cout<<ans;
return 0;
}