codeforces 543C C. Remembering Strings(状态压缩dp)

题目链接:

codeforces 543C


题目大意:

给出n个长度为m的字符串,要求每个字符串必须有一位是区别于其他字符串的,我们修改某个字符串的某一位有一个固定的花费,问修改成符合要求的字符串的最小花费。


题目分析:

  • 我们定义dp[i][state]表示前i字符串达到state状态时的最小花费。
  • 对于一个不符合要求的字符串,我们可以通过修改它的某一位字符达到符合要求,因为字符串不超过小写字母的个数。
  • 可以通过当前要转换为合法状态字符串的某一位,或者直接修改与它在这一位具有相同字符的除去最大的字符串的其他串,顺便将这一组字符串变为合法。
  • 修改第i个字符串的第j个字符时,满足转移方程:
    dp[i][state|(1<<j)]=min{dp[i1][state]+cost[i][j]}
  • 修改一组字符串的第j个字母使他们全部合法的转移方程是:
    dp[i][state|set]=min{dp[i1][state]+costset[j]}

    我们只需要枚举每一个状态即可。

AC代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

int dp[1<<21];
char s[26][26];
int c[26][26];
int v[26][26];
int state[26][26];
int n,m;

int main ( )
{
    while ( ~scanf ( "%d%d" , &n , &m ) )
    {
        for ( int i = 0 ; i < n ; i++ )
            scanf ( "%s" , s[i] );
        for ( int i = 0 ; i < n ; i++ )
            for ( int j = 0 ; j < m ; j++ )
               scanf ( "%d" , &c[i][j] );
        int total = (1<<n)-1;
        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;
                    v[i][k] += c[j][k];
                    maxn = max ( c[j][k] , maxn );
                    state[i][k] |= 1<<j;
                }
                v[i][k] -= maxn;
            }
        memset ( dp , 0x3f , sizeof ( dp ) );
        dp[0] = 0;
        for ( int i = 0 ; i < total ; i++ )
            for ( int j = 0 ; ; j++ )
                if ( !((i>>j)&1))
                {
                    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] + v[j][k] );
                    }
                    break; 
                }
        printf ( "%d\n" , dp[total] );
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值