1. 程式人生 > >51nod 1447 好記的字串 狀壓dp

51nod 1447 好記的字串 狀壓dp

題意

現在有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; }