【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
“Make my parents proud,and impress the girl I like.”