【dp每日一题】CF543C. Remembering Strings

大意:

给出n个字符串,每个字符串的长度均为m,以及一个矩阵,代表修改每个字符串上每个值所需要的花费,现在要求每个字符串至少有一个位置上的值是和别的所有字符串都不一样的,问最少的花费((1 leq n,mleq 20)

思路:

由于n很小,可以想到状压dp,定义dp[state]为当前状态下的最少花费,“状态”代表有哪些字符串已经符合要求,例如101代表第1个和第3个字符串已经是有某一位独一无二了,那么最后的结果就是dp[1<<n-1]。

至于怎么转移,我们可以发现,因为n很小,小于26,所以可以随意修改字符,而不需要担心是否修改后和别的字符串重复,那么对于某一个字符串没有达到条件,我们有两种方法可以进行修改:

第一种:选择某1位,修改这个字符串的这一位。

第二种:选择某一位,将和这一位相同的其他字符串全部修改为独一无二的值,这样还需要维护一个数组,代表修改与第i个字符串的第k个位置相同的其他字符串,需要多少花费。

需要注意的是,应该是在state从小到大的同时,j也从小到大遍历,保证传递性

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int n, m,c[25][25],sumc[25][25],state[25][25],dp[1<<25];
string s[25];
int main(){
    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 >> c[i][j];
        }
    }
    for (int i = 0; i < n;i++){
        for (int k = 0; k < m;k++){
            int maxn = 0;  //记录相同的元素里面修改需要最大的值,留着不改
            for (int j = 0; j < n;j++){
                if(s[i][k]!=s[j][k]){
                    continue;
                }
                sumc[i][k] += c[j][k];
                maxn = max(c[j][k], maxn);
                state[i][k] |= (1 << j);
            }
            sumc[i][k] -= maxn;
        }    
    }
    int T = (1 << n) - 1;
    for (int i = 0; i <= T;i++){
        dp[i] = 0x3f3f3f3f;
    }
    dp[0] = 0;
    for (int i = 0; i < T;i++){
        for (int j = 0; j < n;j++){
            if((i&(1<<j))==0){      //每次优先更新最前面的,保证传递性
                int a = 1 << j;
                for (int k = 0; k < m;k++){
                    int b = state[j][k];
                    dp[i | a] = min(dp[i | a], dp[i] + c[j][k]);
                    dp[i | b] = min(dp[i | b], dp[i] + sumc[j][k]);
                }
                break;
            }
        }
    }
    cout << dp[T] << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/14077413.html