[51nod1447]好记的字符串

来源:互联网 发布:网络主播收入排行榜 编辑:程序博客网 时间:2024/06/04 01:29

Description

给出n个字符串,每个字符串长度均为m
一个字符串为好记的当且仅当它存在一个位置,使得这个位置上它的字符和其他所有串不一样
你每次可以修改某一个字符串中的某一个位置,代价为ai,j
求把所有串变成好记的的最小代价
n,m<=20

Solution

一眼看成好吃的字符串emmmm(UUZ附身)
看到这道题n这么小就知道是状压了。。。
本来我是只会2^n*n^2的做法的,反正能卡过23333
结果被张俊教做题_ (:з」∠) _少掉一个n
考虑设Fs表示当前所有字符串的状态为s,0表示还没有变得好吃,1表示已经变得好吃
然后枚举某一位,考虑把这一位变得好吃的方案
发现只有两种本质不同的方案,1是把它自己改掉,2是选择某一列,把这一列和它相同字符的全部改掉
第二种方法显然是保留最大值
然后考虑优化,发现最后我们要把所有的0变成1,然后选择的列的顺序是没有影响的
于是我们每次转移的时候可以强制把二进制为最低的一个0给消掉
这样复杂度就少掉一个n Orz
虽说这样的套路在NOIP的时候已经见过了

Code

#include <cstdio>#include <cstring>#include <algorithm>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,a,b) for(int i=a;i>=b;i--)using namespace std;#define min(a,b) (a<b?a:b)const int N=25,M=(1<<20)+5,inf=1e9;int n,m,two[N],a[N][N],id[N][N],f[M],b[N],cnt[N][N];char st[N][N];int main() {    scanf("%d%d",&n,&m);    two[0]=1;fo(i,1,n) two[i]=two[i-1]<<1;    fo(i,1,n) scanf("%s",st[i]+1);    fo(i,1,n) b[i]=inf;    fo(i,1,n)        fo(j,1,m) {            scanf("%d",&a[i][j]);            b[i]=min(b[i],a[i][j]);        }    fo(j,1,m) {        fo(k,'a','z') {            int x=0,sum=0,mx=0;            fo(i,1,n) if (st[i][j]==k) x+=two[i-1],sum+=a[i][j],            mx=max(mx,a[i][j]);            fo(i,1,n) if (st[i][j]==k) cnt[i][j]=sum-mx,id[i][j]=x;        }    }    int res=0;bool pty;    fo(i,1,n) {        fo(j,1,m) {            pty=1;            fo(k,1,n) if (k!=i&&st[i][j]==st[k][j]) {pty=0;break;}            if (pty==1) break;        }        if (pty) res+=two[i-1];    }    fo(i,0,two[n]-1) f[i]=inf;f[res]=0;    fo(i,0,two[n]-2) {        if (f[i]==inf) continue;        int st=0;        fo(j,1,n) if (!(i&two[j-1])) {st=j;break;}        f[i|two[st-1]]=min(f[i|two[st-1]],f[i]+b[st]);        fo(j,1,n) f[i|id[st][j]]=min(f[i|id[st][j]],f[i]+cnt[st][j]);    }    printf("%d\n",f[two[n]-1]);}
原创粉丝点击