codeforces C. Remembering Strings 状态压缩-预处理

状态压缩-预处理

题意:

​ 好记的字符串是n个字符串中,每一个字符串至少有一位是与众不同的。现在给你n个字符串,但是有的字符串不满足与众不同,让你修改某一个或者一些位置的字符串使得满足题意,每一个位置的修改都有对应的花费,问最少的花费是多少。

思路:

​ 之前写过的棋盘问题,条件都是直接看的到的,在进行状态压缩的时候直接可以用到条件,但这种题似乎很难直接想到如何使用条件,不妨先思考如何修改,对于一个字符串,修改自己的某一位的花费是直接可以算的,但是可能会自己修改的花费巨大,大于把与其它字符串同位置的修改的和,所以这两种方式的修改都要进行dp取最优。

​ 第一种很简单不多说。

​ 第二种:当修改i行的某一位的字符时,可以把最大的花费不修改,而修改其它行的此位置的字符,修改完之后,对应的行全部都是满足题意的(因为n小于26)。这些需要的花费和状态通过sum和state数组进行保存。

实现过程:

​ 通过从初始状态依次取最优解,直到所有的状态都是最优的。

  • 遇到此类问题,先思考如何解决,再想如何状态压缩也是另一种很好的思维。
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 25;
const int INF = 0x3f3f3f3f;

int n,m;
int c[maxn][maxn];
int sum[maxn][maxn];
int state[maxn][maxn];
int dp[(1<<21)];
char str[maxn][maxn];

int main()
{
    //freopen("in.txt","r",stdin);

    scanf("%d%d",&n,&m);
    for(int i = 0;i < n; i++) {
        scanf("%s",str[i]);
    }
    for(int i = 0;i < n; i++) {
        for(int j = 0;j < m; j++) {
            scanf("%d",&c[i][j]);
        }
    }
    for(int i = 0;i < n; i++) {
        for(int k = 0;k < m; k++) {
            int M = 0;
            for(int j = 0;j < n; j++) {
                if(str[i][k] == str[j][k]) {
                    M = max(M,c[j][k]);
                    sum[i][k] += c[j][k];
                    state[i][k] |= (1<<j);
                }
            }
            sum[i][k] -= M;
        }
    }
    memset(dp,INF,sizeof(dp));
    int pos = (1<<n)-1;
    dp[0] = 0;
    for(int i = 0;i <= pos; i++) {
        for(int j = 0;j < n; j++) {
            if(!(i&(1<<j))) {
                int now = (1<<j);
                for(int k = 0;k < m; k++) {
                    dp[i|now] = min(dp[i|now],dp[i] + c[j][k]);
                    dp[i|state[j][k]] = min(dp[i|state[j][k]],dp[i]+sum[j][k]);
                }
            }
        }
    }
    printf("%d\n",dp[(1<<n)-1]);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值