题意:输入n,m,表示有n个地点,m为最大的花费。接着输入n个地点的坐标x,y,起点为第一个地点。还有n个地点的花费。第一问求,每个人有m个钱,求访问完n个点至少需要多少个人。 第二问是可以有任意多个人,求从起点出发遍历n个点后再回到起点的最少距离。
分析:第一问可以状态压缩后,进行01背包。第二问先找到一个人遍历(1~n)个点再回到起点的位置的最短距离,然后把一个人到达n个点的距离进行分解。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include<cmath> using namespace std; const int maxn = 20; const int oo = 1<<30; int x[maxn], y[maxn], c[maxn]; int ok[1<<17], dis[20][20]; int best[1<<17], tem[20][1<<17]; int sta[1<<17], len; void Init(int n) { int End = (1<<n); for(int i = 0; i<End; i++) best[i] = oo; for(int i = 0; i<n; i++) { for(int j = 0; j<End; j++) tem[i][j] = oo; } tem[0][1] = 0; } int Judge(int state, int n, int m) { int sum = 0; for(int i=0; i<n; i++) if(state&(1<<i)) sum+=c[i]; return sum<=m; } int cal(int i,int j) { return ceil(sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]))); } int DP(int n) { int dp[1<<17]; fill(dp, dp+(1<<n), oo); int state = (1<<n)-1; dp[0] = 0; for(int i=0; i<len; i++) { for(int j=state; j>=0; j--) { if(dp[j]!=oo&&!(sta[i]&j)) dp[sta[i]+j]=min(dp[sta[i]+j], dp[j]+1); } } return dp[state]; } int Tsp(int n) { int state = (1<<n)-1; for(int i=0; i<=state; i++) { if(ok[i]) { for(int j=0; j<n; j++) { if(i&(1<<j)) { best[i] = min(best[i], tem[j][i]+dis[j][0]); for(int k=0; k<n; k++) { if(!(1&(1<<k))) { tem[k][i|(1<<k)]=min(tem[k][i|(1<<k)], tem[j][i]+dis[j][k]); } } } } } } for(int i=1; i<=state; i++) { if(i&1) { for(int j=i&(i-1); j; j=i&(j-1)) { best[i] = min(best[i], best[(i-j)|1]+best[j]); } } } return best[state]; } int main() { int n, m; while(~scanf("%d %d", &n, &m)) { for(int i=0; i<n; i++) scanf("%d %d", &x[i], &y[i]); for(int i=0; i<n; i++) scanf("%d", &c[i]); len =0; for(int s=0; s<(1<<n); s++) { ok[s] = Judge(s, n, m); if(ok[s]) sta[len++] = s; } int ans = DP(n); Init(n); for(int i=0; i<n; i++) { for(int j=0; j<n; j++) { dis[i][j] = cal(i, j); } } if(ans == oo) { puts("-1 -1"); continue; } printf("%d %d ", ans, Tsp(n)); } return 0; }