【51NOD1447】好记的字符串

题面

现在有n个长度一样的字符串,我们说这些字符串是好记的当且仅当,每一个字符串存在一个位置i,其它字符串在i位置的字符和它不一样。
例如{"abc", "aba", "adc", "ada"}这些字符串是不好记的。
而{"abc", "ada", "ssa"}这些是好记的:

对于第一串,在第3个位置,只有它有c;

对于第二个串,在第2个位置,只有它有d;

对于第三个串,在第2个位置,只有它有s;

现在给你n个字符串,你要做一些小的修改使得他们好记。修改第i个字符串的第j个位置要花费aij。那么想要这些字符串都好记,最少的花费是多少呢?
样例解释:把第一列的前三个a改成b,c,d。
n, m (1 ≤ n, m ≤ 20),表示有n个字符串,他们的长度都是m。
ai1, ai2, ..., aim (0 ≤ aij ≤ 10^6)。

分析

至少存在一个位置所有的字母都不同才行,那就枚举每个位置,求出使这一位上的字母完全不同的最大值,然后字母间的冲突用二进制来处理。
挂了...
放弃挣扎,啃题解,状压不是我等蒟蒻能轻易学得炉火纯青的啊...
还有一个隐藏条件,n<=20,说明你找到相同的字母,一定有存在的改法使这个位置不冲突。因为字母有26个啊。所以可以放心地随便改了。
1.将这个位置上有这个重复字母的串全部修改。让这些串都好记。
2.只修改这一个串,使这一个串好记。
对于修改1,需要额外维护位置j的字母i的位置在哪些地方 用01串存,还需要维护修改这些字母的总花费和最大花费,我们就不修改花费最大的那个串。
f[i]存一个数,其二进制形式的每一位k表示第k+1个串是否好记了。每次取出状态中的不好记的串尝试对它分别进行两种修改即可转移。

代码

#include<bits/stdc++.h>
using namespace std;
#define N 28
int k,n,m,mx,tmp,notused=1234567890;
int f[1<<21],a[N][N],maxx[N][N],sum[N][N],mp[N][N];
char s[N][N];
int main()
{
    scanf("%d%d",&n,&m);
    mx=(1<<n)-1;
    for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
    for(int i=1;i<=mx;i++)f[i]=notused;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&a[i][j]);
            sum[j][s[i][j]-'a']+=a[i][j];
            maxx[j][s[i][j]-'a']=max(maxx[j][s[i][j]-'a'],a[i][j]);
            mp[j][s[i][j]-'a']+=(1<<(i-1));
        }    
    for(int i=0;i<=mx;i++)
    {
        if(f[i]==notused)continue;
        for(int j=0;j<n;j++)if((i&(1<<j))==0){k=j+1;break;}
        for(int j=1;j<=m;j++)
        {
            f[i|(1<<k-1)]=min(f[i|(1<<k-1)],f[i]+a[k][j]);
            tmp=f[i]+sum[j][s[k][j]-'a']-maxx[j][s[k][j]-'a'];
            f[i|mp[j][s[k][j]-'a']]=min(f[i|mp[j][s[k][j]-'a']],tmp);
        }
    }
    printf("%d\n",f[mx]);
}

送一组debug时花10积分下的小数据

input
5 2
aa
aa
ab
bb
bb
1 100
100 100
1 1
100 100
100 1
output
4

 

posted @ 2018-10-15 10:53  WJEMail  阅读(174)  评论(0编辑  收藏  举报