感觉也是一个小清新题。。
我们考虑设立状态 $Dp[i][s]$ 表示考虑了前 $i$ 个商店后,购买状态为 $s$ 的最小花费。
转移的话就枚举每个商店 $i$,首先令:
$$Dp[i][s] = Dp[i - 1][s] + D[i]$$
这个过程表示到达这个商店。
然后枚举每个状态 $s$,然后枚举每个不在 $s$ 里的物品 $j$,令:
$$Dp[i][s + \{j\}] = min(Dp[i][s + \{j\}], Dp[i][s] + Cost[i][j])$$
这个过程就相当于是进行了一次 01 背包。
最后还要令 $Dp[i][s] = min(Dp[i][s], Dp[i - 1][s])$ 看看在商店 $i$ 时的购买计划是否划算。
令全集是 $S$,那么最后答案就是 $Dp[n][S]$ 了。
时间复杂度 $O(nm2^m)$,空间复杂度 $O(n2^m)$。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <cstdio> 2 #define min(a, b) ((a) < (b) ? (a) : (b)) 3 #define N 100 + 5 4 #define M 16 + 5 5 #define SIZE 1 << 16 6 #define INF 593119681 7 8 int n, m, W[N], Map[N][M], Dp[N][SIZE]; 9 10 int main() 11 { 12 scanf("%d%d", &n, &m); 13 for (int i = 1; i <= n; i ++) 14 { 15 scanf("%d", W + i); 16 for (int j = 1; j <= m; j ++) 17 scanf("%d", Map[i] + j); 18 } 19 for (int s = 0; s < (1 << m); s ++) 20 Dp[0][s] = INF; 21 Dp[0][0] = 0; 22 for (int i = 1; i <= n; i ++) 23 { 24 for (int s = 0; s < (1 << m); s ++) 25 Dp[i][s] = Dp[i - 1][s] + W[i]; 26 for (int j = 1; j <= m; j ++) 27 for (int s = 0; s < (1 << m); s ++) 28 if ((s & (1 << j - 1)) == 0) 29 Dp[i][s ^ (1 << j - 1)] = min(Dp[i][s ^ (1 << j - 1)], Dp[i][s] + Map[i][j]); 30 for (int s = 0; s < (1 << m); s ++) 31 Dp[i][s] = min(Dp[i][s], Dp[i - 1][s]); 32 } 33 printf("%d\n", Dp[n][(1 << m) - 1]); 34 35 return 0; 36 }