HDU—5067

问题描述:

给定一个n*m大小的图(1<=n,m<=50),图上有一些点>0表示该点需要挖坑(点的总数不超过10),现在从起点(1,1)出发,求得只经过所有点一遍再回到起点的最短距离。

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

  • //poj5067
    //状压dp经典问题----TSP问题
    //
    #include<iostream>
    #include<cmath>
    #include<cstring>
    using namespace std;
    int count;
    int p[11][2];//所有点的坐标
    int dp[1<<11][11];//每种状态下到点i或者说每种状态下从点i开始的最短距离
    int dist[11][11];//所有点之间的距离
    void init(int pnum)
    {
        for(int i=0;i<=pnum;i++)
        {
            for(int j=1;j<=pnum;j++)
            {
                if(i==0)//从起点(1,1)遍历到的点的距离(可以直接-2计算)
                {
                    dist[j][i]=dist[i][j]=p[j][0]+p[j][1]-2;
                }
                else//其余的点之间的距离都可以等于(横坐标之差+纵坐标之差),其实从起点开始的也可以直接写这个公式
                {
                    dist[j][i]=dist[i][j]=abs(p[j][0]-p[i][0])+abs(p[j][1]-p[i][1]);
                }
            }
        }
    }
    int main()
    {
        int n,m;
        while(cin>>n>>m)
        {
            memset(dist,0,sizeof dist);//初始化所有点之间的距离
            memset(dp,0,sizeof dp);//初始化状压数组
            int pnum=0;//表示记录点的数量
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=m;j++)
                {
                    int num;
                    cin>>num;
                    if(num>0)
                    {
                        //把每个需要挖掘的点存入数组p中(起点(1,1)除外)
                        if(i==1&&j==1)continue;
                        pnum++;
                        p[pnum][0]=i;//横坐标存起来
                        p[pnum][1]=j;//纵坐标存起来
                    }
                }
            }
            init(pnum);//计算所有挖掘点之间的距离
            int statenum=1<<pnum;//确定状压的范围(这里点的个数最多也就10个,2的10次方=1024完全可以装的下,也跑得完)
            for(int s=0;s<=statenum;s++)//开始遍历每种情况
            {
                for(int i=1;i<=pnum;i++)//求每种情况与点i的关系,用1的(i-1)进制与s进行位与,求得在此情况下,点i是否被选中
                {
                    int tmp=1<<(i-1);
                    if(s&tmp)//如果被选中
                    {
                        if(s==tmp)//直接判断这种情况是不是只有点i被选中(节省循环时间)
                        {
                            dp[s][i]=dist[0][i];//dp表示该状态下到达某点i的最短距离(也可以说是从点i开始的)
                        }
                        else
                        {
                            dp[s][i]=0x3f3f3f3f;//(有好多点被选中的那种情况,所以先将dp值最大化,然后一步一步取小)
                            for(int k=1;k<=pnum;k++)//再次遍历这pnum个点,计算其他状态下到达k点+k点到达点i的距离,一步一步取小dp值
                            {
                                if(s&(1<<(k-1))&&k!=i)
                                {
                                    dp[s][i]=min(dp[s][i],dp[s^(1<<(i-1))][k]+dist[k][i]);//s异或1<<(i-1)的意义就是传入从其他状态下(没有到达过点i的状态)到达点k的距离,再加上一个点k到达点i的距离,就是一个s状态下起点到达点i的新距离,取小
                                }
                            }
                        }
                    }
                }
            }
            int ans=0x3f3f3f3f;//先将答案最大化
            for(int i=1;i<=pnum;i++)
            {
                ans=min(ans,dp[statenum-1][i]+dist[0][i]);//取所有点都遍历的状态下(除起点外)到达点i的距离+起点到达点i的距离
                //取小
            }
            if(ans==0x3f3f3f3f)ans=0;//如果还等于一开始最大化的值,说明没有所有只走一遍的情况
            cout<<ans<<endl;
        }
    
        return 0;
    }
    

    后续会补上TSP相关的总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值