Codeforces 543C Remembering Strings(DP)

题意比较麻烦

见题目链接


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;
}
View Code
原文地址:https://www.cnblogs.com/keam37/p/4515538.html