胖虎的菜品
题目描述
胖虎惊喜地发现了有N个窗口有他最喜欢吃的菜(其实都喜欢?),但是他想在移动最短距离(毕竟走多了也是会累的)的情况下吃到所有他喜欢吃的菜品,饥饿的他已经没有力气写出代码来计算自己的最佳方法了,所以他找到了你,来帮他解决这个问题。现在给你N个窗口及窗口的坐标,请输出胖虎所要移动的最小距离总和。
胖虎一开始在(0,0)点处。
输入
第一行一个正整数n(1≤n≤13) 。
接下来每行2个实数,表示第i块窗口的坐标。
两点之间的距离公式为。
输出
一个数,表示要跑的最少距离,保留2位小数。
样例输入
4
1 1
1 -1
-1 1
-1 -1
样例输出
7.41
思路分析
旅行商问题(TSP)的变种问题,采用状态压缩DP。
1.读入n个点的横纵坐标(0-based indexing)。
2.计算n个点相互之间的直线距离,点i与点j之间的直线距离就是dist[i][j]。
3.dp[s][i]表示在状态s下,到达i点的最短距离。s是一个二进制数(位表示访问状态),如0b1011,包含0,1,3(
)。记得初始化dp数组为较大的数。
当s=
时,dp[s][i]就等于起点(0,0)到点i的直线距离。
4.外层循环遍历状态s(
),内层循环遍历n个点(
),如果状态s包含点i,计算前一个状态s0=s^(1<<i)(即去掉点i的状态),遍历s0中所有可能的j点(
),如果s0包含j点,则更新dp[s][i]=min(dp[s0][j]+dist[i][j],dp[s][i])。
5.遍历n个点,比较dp[1<<n-1][i],找到经过所有点后、以点i为终点的最短路程。
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n;
double x[14],y[14],res=1e9,dist[14][14];
int main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>n;for(int i=0;i<n;i++){cin>>x[i]>>y[i];}for(int i=0;i<n;i++){for(int j=0;j<n;j++){dist[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));dist[j][i]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));}}vector<vector<double>>dp(1<<14,vector<double>(14,1e9));for(int i=0;i<n;i++){dp[1<<i][i]=sqrt(x[i]*x[i]+y[i]*y[i]);}for(int s=1;s<(1<<n);s++){for(int i=0;i<n;i++){if(s&(1<<i)){int s0=s^(1<<i);for(int j=0;j<n;j++){if(s0&(1<<j)){dp[s][i]=min(dp[s0][j]+dist[i][j],dp[s][i]);}}}}}int full=(1<<n)-1;for(int i=0;i<n;i++){res=min(dp[full][i],res);}cout<<fixed<<setprecision(2)<<res;return 0;
}