状态压缩方程求解最少次数问题

//状态压缩方程的计算

#include<iostream>

#include<cstring>

#include<algorithm>

using namespace std;

const int INF = 0x3f3f3f;

int a[20][20];

int dp[1 << 16][20]; //第一个状态保存的是要选择的哪些元素,第二个状态 保存的是最后状态

// 抽象来说就是,一个物品经过了哪些人的手最后第二个维度代表最终到谁手里

int main()

{

 int n;

 cin >> n;

 for(int i = 0;i < n;i++){

  for(int j = 0;j < n;j++){

   cin >> a[i][j];

  }

 }

 memset(dp,0x3f,sizeof(dp));

 for(int i = 0;i < n;i++){

  dp[1 << i][i] = 0;

 }

 for(int i = 0;i <(1 << n);i++){

  for(int j = 0;j < n;j++){

   if(i & (1 << j)){

    for(int k = 0;k < n;k++){

     if(!(i & (1 << k))){

      dp[i | 1 << k][k] = min(dp[i | 1 << k][k],dp[i][j] + a[j][k]);

     }

    }

   }

  }

 } 

 int ans = INF;

 for(int i = 0;i < n;i++){

  ans = min(ans,dp[(1 << n)- 1][i]);

 }

 cout << ans <<endl;

 return 0;

 } 

这段代码的目的是计算一个动态规划问题中的最优值,其中使用了状态压缩技巧。

假设有 n 个元素需要进行处理,每个元素都有不同的权重或者代价,代码中的 dp 数组用于保存每种状态下的最优值。

这里使用了二进制位来表示状态,i 是一个二进制数,它的每个位表示一个元素的选择状态。如果第 j 位为 1,表示第 j 个元素被选择,否则表示未选择。

接下来,我们来解析代码的具体逻辑:

外层循环:for(int i = 0; i < (1 << n); i++)

这个循环遍历了所有可能的状态。由于有 n 个元素,所以状态总数为 2^n,即 (1 << n)。

第一层内层循环:for(int j = 0; j < n; j++)

这个循环遍历了每个元素作为当前状态的起始点。

条件判断:if(i & (1 << j))

这个条件判断语句检查当前状态 i 的第 j 位是否为 1,即判断第 j 个元素是否在当前状态中被选择。

第二层内层循环:for(int k = 0; k < n; k++)

这个循环遍历了除了已经选择的元素外的其他元素,以寻找下一个要添加到状态中的元素。

第二个条件判断:if(!(i & (1 << k)))

这个条件判断语句检查当前状态 i 的第 k 位是否为 0,即判断第 k 个元素是否未被选择。这是为了避免将已经选择过的元素再次添加到状态中。

状态转移方程:dp[i | (1 << k)][k] = min(dp[i | (1 << k)][k], dp[i][j] + a[j][k])

这个方程表示更新状态 i 加上选择第 k 个元素后的状态 i | (1 << k) 的最优值。dp[i][j] 表示当前状态 i 选择第 j 个元素的最优值,a[j][k] 表示从元素 j 到元素 k 的转移代价。

该段代码的目的是通过遍历所有可能的状态,并根据转移方程更新每个状态的最优值。最终,dp[(1 << n) - 1][k] 就是所求问题的最优解。

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值