郊区春游题解

郊区春游

题意:

给定一张图,求从某个起点出发,经过其中R个点(R个点给出)的最短路径(每个点经过且只经过一遍)

题解:

首先我们用floyed处理出任意两点的距离
dp[i][j]表示当前状态为i,当前所在的目标城市为j的最短路径
i为01串,1表示该点走过,0表示没走过
从点x到点y,
城市x已经走过:城市x是编号j的,所以(i&(1<<j))== 1,i的第j位是1,说明x走过
y还未走,所以(i&(1<<k)) == 0,i的第k位是0,说明y还没走,下一步可以走
转移方程:

dp[i+(1<<k)][k]=min(dp[i+(1<<k)][k],dp[i][j]+dist[x][y]);

代码:

#include<bits/stdc++.h>
#define debug(a,b) printf("%s = %d\n",a,b)
typedef long long ll;
using namespace std;

inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);
   return s*w;
}
const int inf=0x3f3f3f;
const int maxn=210;
int dp[41000][17];
int dis[maxn][maxn];
int a[maxn];
int main()
{
	memset(dis,inf,sizeof(dis));
	memset(dp,inf,sizeof dp);
	int n,m,r;
	cin>>n>>m>>r;
	for(int i=0;i<r;i++)cin>>a[i];
	for(int i=1;i<=m;i++)
	{
		int x,y,w;
		cin>>x>>y>>w;
		dis[x][y]=dis[y][x]=w;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++)
		{
			for(int k=1;k<=n;k++)
			{
				if(i==j||j==k||i==k)continue;
				dis[j][k]=min(dis[j][k],dis[j][i]+dis[i][k]);
			}
		}
	}
	for(int i=0;i<r;i++)
		dp[1<<i][i]=0;//设置起点 
		int x,y;
	for(int i=1;i<(1<<r);i++)//枚举所有状态
	{
		for(int j=0;j<r;j++)//枚举出发点 
		{
			if(!(i&(1<<j)))continue;//如果j没走过 
			x=a[j];
			for(int k=0;k<r;k++)//枚举到达点 
			{
				if(i&(1<<k))continue;//如果k还未走过 
				y=a[k];
				dp[i|(1<<k)][k]=min(dp[i|(1<<k)][k],dp[i][j]+dis[x][y]);
			}
		}
	}
	int minx=inf;
	for(int i=0;i<r;i++)
		minx=min(minx,dp[(1<<r)-1][i]);
	//(1<<r)-1表示r个点都经过的情况
	cout<<minx; 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值