关于状压dp

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=20,M=1<<N;

int f[M][N],w[N][N];//w表示的是无权图

int main()
{
    int n;
    cin>>n;

    for(int i=0;i<n;i++)
     for(int j=0;j<n;j++)
      cin>>w[i][j];

    memset(f,0x3f,sizeof(f));//因为要求最小值,所以初始化为无穷大
    f[1][0]=0;//因为零是起点,所以f[1][0]=0;

    for(int i=0;i<1<<n;i++)//i表示所有的情况
     for(int j=0;j<n;j++)//j表示走到哪一个点
      if(i>>j&1)
       for(int k=0;k<n;k++)//k表示走到j这个点之前,以k为终点的最短距离
        if(i>>k&1)
         f[i][j]=min(f[i][j],f[i-(1<<j)][k]+w[k][j]);//更新最短距离

    cout<<f[(1<<n)-1][n-1]<<endl;//表示所有点都走过了,且终点是n-1的最短距离
    //位运算的优先级低于'+'-'所以有必要的情况下要打括号
    return 0;
}

作者:E.lena
链接:https://www.acwing.com/solution/content/18533/
来源:AcWing

f[i][j]是一个二维数组,它的意思是从集合i出发,以j为终点的最短距离。这个数组是用来存储动态规划的状态的,它的大小是M*N,其中M是1<<N,表示所有可能的集合的个数,N是点的个数。这个数组的每一个元素都是由状态转移方程来计算的,即f[i][j]=min(f[i-(1<<j)][k]+w[k][j]),其中k是任意一个在集合i中的点,w[k][j]是从k到j的距离。这个数组的最终目标是求出f[(1<<N)-1][N-1],即从集合{0,1,…,N-1}出发,以N-1为终点的最短距离。这个问题也叫做旅行商问题,它是一个经典的NP-hard问题,没有多项式时间的解法。

代码要特地声明一下f[1][0]=0,是因为这是一个初始状态,表示从集合{0}出发,以0为终点的最短距离为0。这是一个显然的事实,因为不需要走任何路程就可以到达自己。其他的f[i][j]都是由状态转移方程来计算的,所以不需要特地声明。比如f[2][1]表示从集合{1}出发,以1为终点的最短距离,它是由f[2-(1<<1)][0]+w[0][1]得到的,即f[0][0]+w[0][1]。但是f[0][0]是没有意义的,因为空集不能出发也不能到达任何点,所以它被初始化为无穷大。w[0][1]表示从0到1的距离,它是由输入给定的。所以f[2][1]就等于无穷大加上w[0][1],仍然是无穷大。这表示从集合{1}出发,以1为终点的路程是不可能的。你可以想象一下,如果你只能走第一个点,你怎么能到达第二个点呢?所以代码只声明了f[1][0]=0,其他的都是由动态规划的思想来求解的。

i>>j&1是一个位运算表达式,它的意思是将i的二进制形式右移j位,然后与1进行按位与运算。这个表达式的结果是0或1,表示i的第j位是0还是1。例如,如果i=5,j=2,那么i>>j&1的结果是1,因为5的二进制形式是101,右移两位后变成1,与1进行按位与运算后仍然是1。这个表达式在代码中用来判断某个集合是否包含某个元素,或者某个状态是否包含某个子状态。例如,i表示一个集合,每一位表示一个元素是否在集合中,那么i>>j&1就可以判断第j个元素是否在集合中。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值