题意
有n个地点,每个旅行商在地点上花费最多m,每个地点坐标(xi,yi),花费Ci;
求
1.最少几个旅行商可以走过所有点;
2.所有旅行商走完回到起点的路程长度的总和最小是多少;
思路
第一问
转移方程:dp[i]=min(dp[i],dp[i^state]+1);
第二问
转移方程:dp[i]=min(dp[i],dp[i^state]+cost[state]);
用解tsp的方法预处理出各种状态路程长度的花费,存在cost里;
对于每种状态求出是否满足解题时间不大于m存在state里;
最后把每种可行的state当作物品,全都取当作总容量,用01背包解法解出两个结果;
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=17,M=(1<<16)+1;
const int INF=0x3f3f3f3f;
struct Point
{
int x,y,cost;
} point[20];
int G[N][N];//存邻接矩阵
int state[M];//存单个旅行商状态
int cost[M];//存该状态的花费
int dp[M][N];
int n,m,cnt;
int lengh(int a,int b)
{
double sum=(double)(point[a].x-point[b].x)*(point[a].x-point[b].x)+(point[a].y-point[b].y)*(point[a].y-point[b].y);
return ceil(sqrt(sum));
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i=0; i<n; i++)
scanf("%d %d",&point[i].x,&point[i].y);
for(int i=0; i<n; i++)
scanf("%d",&point[i].cost);
//预处理出邻接矩阵
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
G[i][j]=lengh(i,j);
//求i状态以j为终点旅行商最少花费(路长花费);
memset(dp,0x3f,sizeof dp);
dp[1][0]=0;
for(int i=0; i<(1<<n); i++)
for(int j=0; j<n; j++)
if(i&(1<<j))
for(int k=0; k<n; k++)
if((i^(1<<j))&(1<<k))
dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+G[k][j]);
//求状态i旅行商最少花费(路长花费)(合并时我们不关心单个旅行商的终点在哪)
memset(cost, 0x3f,sizeof cost);
for(int i=0; i<(1<<n); i++)
for(int j=0; j<n; j++)
if((i|1)&(1<<j))
cost[i]=min(cost[i],dp[i|1][j]+G[j][0]); //合并时需要忽略起点0,这里处理时忽略起点0
//求出m花费(解题花费)允许的旅行商可以到达地点的状态
cnt=0;
for(int i=0; i<(1<<n); i++)
{
int sum=0;
for(int j=0; j<n; j++)
if(i&(1<<j))
sum+=point[j].cost;
if(sum<=m)
state[cnt++]=i;
}
//01背包求解最少旅行商数和最小路长花费
memset(dp,0x3f,sizeof dp);
dp[1][0]=dp[1][1]=0;
for(int i=0; i<cnt; i++)
for(int j=(1<<n)-1; j>=0; j--)
if((j|state[i])==j)
{
dp[j][0]=min(dp[j][0],dp[j^state[i]][0]+1);
dp[j][1]=min(dp[j][1],dp[j^state[i]][1]+cost[state[i]]);
}
if(dp[(1<<n)-1][0]==INF)
printf("-1 -1\n");
else
printf("%d %d\n",dp[(1<<n)-1][0],dp[(1<<n)-1][1]);
}
return 0;
}