題意
有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;
}