没有用DP,搞了个搜索
Solution
如果要枚举合法对的话,用脑子想想是很困难的,所以我们正难则反——枚举非法对。
思考一下如果 (a) 和 (b) 对应的数为 (0) 即不能相邻的话,将 (a,b) 中间的字符消完就是非法操作。举个例子: (acadbc) ,那么删除 (d) 就是非法的,而删除 (d) 非法了,删除 (d(b)c) 也是非法的。当然,不能确定 (ad) 是非法操作,因为枚举的时候,非法对的字符不能删。
因为 (pleq 17) ,所以可以用状压来存储状态。所以存所有非法对的状态,从最开始的状态往外搜索即可。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int INF=0x3f3f3f3f,N=1e6+10;
int n,p,P,ans=INF,a[N],tmp[1<<17],f[1<<17],ln[1<<17],g[17][17];
char s[N];
void dfs0(int x){
if(tmp[x]) return ;
tmp[x]=1;
for(int y=x;y;y^=y&-y) dfs0(x^y&-y);
}
void dfs1(int x){
if(f[x]||tmp[x]) return ;
tmp[x]=1;
ans=min(ans,ln[x]);
for(int y=x;y;y^=y&-y) dfs1(x^y&-y);
}
int main(){
scanf("%d%d%s",&n,&p,s+1);P=(1<<p)-1;
for(int i=1;i<=n;i++) ++ln[1<<(a[i]=s[i]-'a')];
for(int i=1;i<=P;i++) ln[i]=ln[i^i&-i]+ln[i&-i];
for(int i=0;i<p;i++)
for(int j=0;j<p;j++) scanf("%d",&g[i][j]);
for(int i=0;i<p;i++)
for(int j=0;j<p;j++)
if(!g[i][j]){
int now=0,pre=-1;
for(int k=1;k<=n;k++){
if(a[k]==i||a[k]==j){
if(a[k]==i&&pre==j||a[k]==j&&pre==i) dfs0(P^now);
now=0,pre=a[k];
}
else now|=1<<a[k];
}
int t=1<<i|1<<j;
for(int k=0;k<=P;k++){
if((k&t)==t) f[k]|=tmp[k];
tmp[k]=0;
}
}
dfs1(P);
printf("%d
",ans);
return 0;
}