hdu-5067(状态压缩dp)

先来了解一个经典问题:TSP
    一个n个点的带权的有向图,求一条路径,使得这条路经过每个点恰好一次,并且路径上边的权值和最小(或者最大)。或者求一条具有这样性质的回路,这是经典的TSP问题。
    n <= 16 (重要条件,状态压缩的标志)
    如何表示一个点集:
由于只有16个点,所以我们用一个整数表示一个点集:
例如:
    5 = 0000000000000101;(2进制表示)
    它的第0位和第2位是1,就表示这个点集里有2个点,分别是点0和点2。
    31 = 0000000000011111; (2进制表示)
    表示这个点集里有5个点,分别是0,1,2,4,5;
所以一个整数i就表示了一个点集;整数i可以表示一个点集,也可以表示是第i个点。


    状态表示:
dp[i][j]表示经过点集i中的点恰好一次,不经过其它的点,并且以j点为终点的路径,权值和的最小值,如果这个状态不存在,就是无穷大。
    状态转移:
    单点集:状态存在dp[i][j] = 0;否则无穷大。非单点集:
    1:状态存在  dp[i][j] = min(dp[k][s] + w[s][j])
    k表示i集合中去掉了j点的集合,s遍历集合k中的点并且dp[k][s]状态存在,点s到点j有边存在,w[s][j]表示边的权值。
    2.:状态不存在 dp[i][j]为无穷大。
    最后的结果是: min( dp[( 1<<n ) – 1][j] ) ( 0 <= j < n );

   
    技巧:利用2进制,使得一个整数表示一个点集,这样集合的操作可以用位运算来实现。例如从集合i中去掉点j:

    k = i & (~( 1<<j)) 或者 k = i - (1<<j)

hdu-5067

题意:给一个n*m方阵,有的点不为0代表有石头,为0代表没石头,求从(0,0)点经过所有有石头的点再回到(0,0)点的最小步数。

比tsp问题多了限制条件即起始点必须为(0,0),终点也为(0,0)。

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<string.h>
using namespace std;
#define N 2048
#define inf 3000
int n,m;
int tot;
int x[15],y[15];
int dp[N][12];
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        tot=0;
        int k;
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
        {
            scanf("%d",&k);
            if(k||i==0&&j==0){
                x[tot]=i;
                y[tot++]=j;//tot个必须走的点,有石头的点和起点
            }
        }
        dp[1][1]=0;
        for(int i=3;i<(1<<tot);i+=2)
            if(i&1){
                for(int j=1;j<tot;j++)
                {
                    int mi=inf;
                    if(i&(1<<j)){
                        int s=i-(1<<j);
                        for(int k=0;k<tot;k++)
                        {
                            if(k==0&&s!=1) continue;//保证起点为第一个点即(0,0)
                            if(s&(1<<k)) mi=min(mi,dp[s][k+1]+abs(y[k]-y[j])+abs(x[k]-x[j]));
                        }
                    }
                    dp[i][j+1]=mi;
                }
            }
            //printf("%d\n",dp[(1<<tot)-1][tot]);
            int mi=inf;
            int s=(1<<tot)-1;
            if(tot==1) mi=0;
            for(int i=1;i<tot;i++)
               mi=min(dp[s][i+1]+x[i]+y[i],mi);//返回终点(0,0)的最小步数
               printf("%d\n",mi);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值