3进制状态压缩DP——HDU3001 Travelling 旅行商问题

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3001


题目大意:n个城市之间有m条路,经过每条路径都有一定的花费,当然有些城市是不可达的。

遍历所有的城市,并且每个城市最多只能遍历2次,求取花费的最小费用并输出,如果找不到这样的路径,输出-1。(n<=10)


解题思路:由于n是小于10的,所以这个题目可以使用状态压缩来做。由于每个城市最多可以遍历2次,所以需要采用3进制的状态压缩。

基本状态转移方程:dp[l][k]=min(dp[l][k],dp[i][j]+Map[j][k])

其中dp[i][j]表示当前状态是i,当前点是j点。

由状态方程可知,我们只需要遍历所有的状态i,以及所有当前点j和所有的下一个点k就可以了。


源代码:

//最多遍历的城市只有10,个那么每一行的状态是非常小的,所以可以使用状态压缩DP

//经典的旅行商问题,每个城市最多走2遍至少走1遍,求最小遍历费用
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#define INF 0x3f3f3f3f      //不可达状态
#include<algorithm>
using namespace std;
int n,m;    //n个城市,m条路,最多10个城市
int Map[11][11];
int dp[60000][11];      //状态为i时并且这个时候正处在j点的时候的最小费用
int Time[60000][11];    //状态为i时遍历j点的次数
int s[11];
void init()
{
    int i,j,t;
    s[0]=1;
    for(i=1;i<=n;i++)
    {
        s[i]=s[i-1]*3;  //求出到达每个点状态的增量
    }
    for(i=0;i<s[n];i++)
    {
        t=i;
        for(j=0;j<n;j++)
        {
            Time[i][j]=t%3;     //求取当状态为i的时候遍历j点的次数
            t=t/3;
        }
    }
    return;
}
void DP()
{
    int i,j,t,k,l,ans,flag;
    ans=INF;
    for(i=0;i<s[n];i++)    //枚举每种状态
    {
        flag=1;
        for(j=0;j<n;j++)    //枚举当前点j
        {
            if(Time[i][j]==0)   {flag=0; continue;}     //状态i不包含j点
            if(dp[i][j]==INF)   continue;   //说明该状态是不存在的
            for(k=0;k<n;k++)    //枚举下一个点k
            {
                if(Map[j][k]==INF)  continue;   //两点不可达
                if(Time[i][k]==2 || j==k)   continue;   //k点已经被遍历了2次
                l=i+s[k];
                dp[l][k]=min(dp[l][k],dp[i][j]+Map[j][k]);
            }
        }
        if(flag)    //这个条件表示i状态包含了所有的点
        {
            for(j=0;j<n;j++)
            {
                ans=min(ans,dp[i][j]);
            }
        }
    }
    if(ans==INF)    printf("-1\n");
    else    printf("%d\n",ans);
    return;
}
int main()
{
    int i,j,k,t;
	//freopen("in.txt","r",stdin);
	while(scanf("%d%d",&n,&m)==2)
	{
	    memset(dp,INF,sizeof(dp));
	    memset(Map,INF,sizeof(Map));
	    memset(Time,0,sizeof(Time));
	    while(m--)
	    {
	        scanf("%d%d%d",&i,&j,&k);
	        i--,j--;        //注意:!!城市是从0开始计数
	        Map[i][j]=Map[j][i]=min(k,Map[i][j]);
	    }
	    init();
	    for(i=0;i<n;i++)    //枚举起点
	    {
	        dp[s[i]][i]=0;   //初始化起点的耗费
	    }
	    DP();
	}
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值