Travelling (HDU 3001)

一开始看成了一个城市最多只旅行一次,还以为是简单题,所以直接DFS,可是一直不对。


后来仔细看,是至少旅行两次,这样用DFS就没法了。



后来查了才知道是TSP问题,用的是DP,其实我也不太会写,先是看了一下别人的原理,代码,


自己也是边写边模仿的吧。,就这样了


原理:这里每个节点的状态可以是已经访问0,1,2次这样的三种状态,所以可以用一个三进制数记录每一个节点的状态。

   pre[i][j]表示:在状态i下第j个城市的状态。(节点是由0~n-1的),  (这个数组的功能是先计算一次,多次使用,可以节约时间)

   dp[i][j]表示:在状态i下最后访问的城市是j。(全部初始化为无穷大,并且只有一个城市的状态的对应的访问的最后城市为0值)

  更新方程:dp[next][k]=max(dp[next][k],dp[cur][j]+map[j][k]);


注意:给出的边有重边,直接取重边中的最小边即可


#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>

using namespace std;

const int inf=0x3f3f3f3f;
const int N = 60000;

int dp[N][12],pre[N][12];
int n,m;
int map[12][12];
int s[12];

void init()
{
	int i,j,k;
	s[0]=1;
	for (i=1;i<=12;i++)
		s[i]=3*s[i-1];
	
	for (i=0;i<s[10];++i)
	{
		for (j=0,k=i;j<10;++j)
		{
			pre[i][j]=k%3;
			k/=3;
		}
	}
}

void DP()
{
	int i,j,k;
	int ans=inf;
	
	for (i=0;i<n;i++)
		dp[s[i]][i]=0;
	
	for (i=0;i<s[n];++i)
	{
		bool isok=true;
		for (j=0;j<n;++j)
		{
			if (!pre[i][j])
				isok=false;
			if (dp[i][j]==inf)
				continue;
			
			for (k=0;k<n;++k) if (j!=k && pre[i][k]!=2 && map[j][k]!=inf)
			{
				int next=i+s[k];
				dp[next][k]=min(dp[next][k],dp[i][j]+map[j][k]);	
			}
		}
		if (isok)
		{
			for (j=0;j<n;++j)
				ans=min(ans,dp[i][j]);	
		}
	}
	
	if (ans!=inf)
		cout<<ans<<endl;
	else
		cout<<-1<<endl;
}

int  main()
{
	freopen("in.txt","r",stdin);
	int i,j,k;
	init();
	while (cin>>n>>m)
	{
		memset(map,inf,sizeof(map));
		for (i=0;i<s[n];++i)
			for (j=0;j<n;j++)
				dp[i][j]=inf;	
			
		while (m--)
		{
			cin>>i>>j>>k;
			i--;
			j--;
			map[i][j]=map[j][i]=min(map[i][j],k);
		}	
		DP();
	}	
	
	
	
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值