状压DP——Travelling ( HDU 3001 )

1.因为每个城市最多访问2次,所以用状态0,1,2记录某个城市被访问的次数,把1~N个城市的访问状态连在一起可以用一个三进制数记录。

2.定义DP[i][j]中i为一个三进制数表示N个城市的访问情况,然后j表示目前访问到的城市序号,那么我们可以得出状态转移方程:
dp[i+3v][v]=min(dp[i+3v][v],dp[i][j]+Cjv)
表示从城市j出发到城市v的情况最小花费。

  • AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#define gets(i,j) (i/len[j])%3  //用于计算压缩状态i下城市j的状态
using namespace std;

int dp[60000][11];
int c[20][20];
int len[11];
int n,m;

int main()
{
    len[0] = 1;
    for(int i=1;i<=10;i++)
        len[i] = len[i-1]*3;
    int n,m,f,u,v,a,b,w,ans;
    while(~scanf("%d%d", &n, &m))
    {
        memset(c, 0x3f3f3f3f, sizeof(c));     //初始化任意城市间道路距离为无穷
        memset(dp, 0x3f3f3f3f, sizeof(dp));   //初始化所有状态为无穷,表示该状态还未到达
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d", &a, &b, &w);
            a--,b--;                    //将城市编号转换成从0开始到n-1
            c[a][b] = c[b][a] = min(c[a][b], w);   //a,b间距离取所有道路的最小值
        }

         ans = dp[0][0];    //最小费用赋值为无穷

        for(int i=0;i<n;i++)   
          dp[len[i]][i] = 0;   //初始化每个城市作为出发点的状态

        for(int i=1;i<len[n];i++)
        {
             f=1;
            for(int j=0;j<n;j++)
            {
                if(gets(i,j) == 0)
                {
                    f = 0;
                    continue;
                }  //在状态i下,最后到达城市为j,转移到其他状态

                for(int v=0;v<n;v++)
                {  //表示城市v在状态i下已经访问过两次,不能再次访问
                    if(gets(i,v) == 2)
                      continue;
                     u = i +len[v];
                    dp[u][v] = min(dp[u][v], dp[i][j]+c[j][v]);
                }
            }
            if(f)
              for(int j=0;j<n;j++)
                ans=min(ans, dp[i][j]);
        }
        if(dp[0][0] == ans)
          ans = -1;
        printf("%d\n", ans);
    }       
    return 0;   
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值