51nod 1447 好記的字串 狀壓dp
阿新 • • 發佈:2019-01-29
題意
現在有n個長度一樣的字串,我們說這些字串是好記的當且僅當,每一個字串存在一個位置i,其它字串在i位置的字元和它不一樣。
例如{“abc”, “aba”, “adc”, “ada”}這些字串是不好記的。
而{“abc”, “ada”, “ssa”}這些是好記的:
· 對於第一串,在第3個位置,只有它有c;
· 對於第二個串,在第2個位置,只有它有d;
· 對於第三個串,在第2個位置,只有它有s;
現在給你n個字串,你要做一些小的修改使得他們好記。修改第i個字串的第j個位置要花費aij。那麼想要這些字串都好記,最少的花費是多少呢?
樣例解釋:把第一列的前三個a改成b,c,d。
1 ≤ n, m ≤ 20
分析
一開始往費用流上面去想了,沒有想到狀壓dp。
顯然對於每一列的某種字元,我可以選擇只改變某一個,使得該字串好記,也可以選擇把全部都改變,使得對應的所有字串都好記。
那麼我們可以狀壓。對於每一個狀態,列舉它的第一個0位選擇哪種方式變得好記,往後轉移即可。
程式碼
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=25;
const int inf=1000000000;
int bin[N ],n,m,a[N][N],f[1048585],b[N][N+5],w[N][N+5],mx[N][N+5];
char s[N][N];
void updata(int &x,int y) {x=min(x,y);}
int main()
{
bin[0]=1;
for (int i=1;i<=20;i++) bin[i]=bin[i-1]*2;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%s",s[i]+1);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
w[j][s[i][j]-'a']+=a[i][j];
mx[j][s[i][j]-'a']=max(mx[j][s[i][j]-'a'],a[i][j]);
b[j][s[i][j]-'a']+=bin[i-1];
}
for (int i=1;i<bin[n];i++) f[i]=inf;
for (int i=0;i<bin[n]-1;i++)
{
if (f[i]==inf) continue;
int k;
for (int j=0;j<n;j++) if (!(i&bin[j])) {k=j+1;break;}
for (int j=1;j<=m;j++)
{
updata(f[i|bin[k-1]],f[i]+a[k][j]);
updata(f[i|b[j][s[k][j]-'a']],f[i]+w[j][s[k][j]-'a']-mx[j][s[k][j]-'a']);
}
}
printf("%d",f[bin[n]-1]);
return 0;
}