题意比较麻烦
Solution:
非常值得注意的一点是题目给出的范围只有20,而众所周知字母表里有26个字母。于是显然对一个字母进行变换后是不影响到其它字符串的。
20的范围恰好又是常见状压DP的范围,所有状态压缩后用DP[sta]代表对应位的字符串已经满足要求的最小花费。
转移的时候,对一个字符串,逐列判断使它满足条件的最小花费,记录使用这个策略对sta的影响。
即对同一列有两种情况,直接变换该字符串的这一位,或者变换这一列的sum-1个有相同字符的位置(去掉代价最大的)。
#include <bits/stdc++.h> using namespace std; int dp[1 << 20]; int cost[30][30], f[30][30], change[30][30]; int n, m; char s[30][30]; inline void getCost () { for (int k = 0; k < n; ++k) { for (int i = 0; i < m; ++i) { int sum = 0, tem = 0; f[i][k] = 0x7fffffff; for (int j = 0; j < n; ++j) { if (s[j][i] == s[k][i]) { change[i][k] |= 1 << j; sum += cost[j][i]; tem = max (tem, cost[j][i]); } } f[i][k] = min (f[i][k], sum - tem); } } } int main() { ios::sync_with_stdio (0); cin >> n >> m; for (int i = 0; i < n; ++i) cin >> s[i]; for (int i = 0; i < n; ++i) for (int j = 0; j < m; ++j) cin >> cost[i][j]; getCost(); memset (dp, 0x3f, sizeof dp); dp[0] = 0; for (int st = 0; st < (1 << n); ++st) { for (int i = 0; i < n; ++i) { if ( (st & 1 << i) == 0) { for (int j = 0; j < m; ++j) { dp[st | change[j][i]] = min (dp[st | change[j][i]], dp[st] + f[j][i]); dp[st | 1 << i] = min (dp[st | 1 << i], dp[st] + cost[i][j]); } break; } } } cout << dp[ (1 << n) - 1] << endl; }
时间: 2024-12-21 09:13:42