链接:点击打开链接
题意:有n个点,每次可以从起点出经过一个点或者两个点回到起点,问所有点都经过后所需最小的路径和
代码:
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
struct node{
int x,y;
}s[50];
int dp[1<<21],pre[1<<21],dis[50][50];
bool cmp(int a,int b){
return (a&(-a))<(b&(-b));
}
int main(){
int t,n,i,j,k,cas,sta,siz,num;
cas=1;
scanf("%d",&t);
while(t--){
vector<int> G;
scanf("%d%d",&s[0].x,&s[0].y);
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d%d",&s[i].x,&s[i].y);
for(i=0;i<=n;i++)
for(j=i;j<=n;j++)
dis[i][j]=dis[j][i]=(s[i].x-s[j].x)*(s[i].x-s[j].x)+(s[i].y-s[j].y)*(s[i].y-s[j].y);
memset(dp,INF,sizeof(dp));
memset(pre,0,sizeof(pre));
dp[0]=0; //dp[s]表示状态是s时最小路径和
for(sta=0;sta<(1<<n);sta++){
for(j=1;j<=n;j++){
if(sta&(1<<(j-1)))
continue;
if(dp[sta|(1<<(j-1))]>dp[sta]+dis[0][j]*2){
dp[sta|(1<<(j-1))]=dp[sta]+dis[0][j]*2;
pre[sta|(1<<(j-1))]=sta;
} //每次搬一个
for(k=j+1;k<=n;k++){
if(sta&(1<<(k-1)))
continue;
if(dp[sta|(1<<(j-1))|(1<<(k-1))]>dp[sta]+dis[0][j]+dis[j][k]+dis[0][k]){
dp[sta|(1<<(j-1))|(1<<(k-1))]=dp[sta]+dis[0][j]+dis[j][k]+dis[0][k];
pre[sta|(1<<(j-1))|(1<<(k-1))]=sta;
} //每次搬两个
} //用pre数组记录每个状态上一个状态,最后用一次异或求出
} //用了哪个点
}
printf("Case %d:\n",cas++);
printf("%d\n",dp[(1<<n)-1]);
for(i=(1<<n)-1;i>0;i=pre[i]){
G.push_back(i^pre[i]);
}
sort(G.begin(),G.end(),cmp); //保证字典序最小,因此判断二进制最后一位的大小
num=0,siz=G.size();
for(i=0;i<siz;i++)
for(j=1;j<=n;j++)
if(G[i]&(1<<(j-1))){
num++;
if(num==n)
printf("%d\n",j);
else
printf("%d ",j);
}
}
return 0;
}