//状态压缩方程的计算
#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] 就是所求问题的最优解。