CF1117F Crisp String

没有用DP,搞了个搜索

Solution

​ 如果要枚举合法对的话,用脑子想想是很困难的,所以我们正难则反——枚举非法对。

​ 思考一下如果 \(a\)\(b\) 对应的数为 \(0\) 即不能相邻的话,将 \(a,b\) 中间的字符消完就是非法操作。举个例子: \(acadbc\) ,那么删除 \(d\) 就是非法的,而删除 \(d\) 非法了,删除 \(d(b)c\) 也是非法的。当然,不能确定 \(ad\) 是非法操作,因为枚举的时候,非法对的字符不能删。

​ 因为 \(p\leq 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\n",ans);
    return 0;
}
posted @ 2020-09-27 10:24  jasony_sam  阅读(121)  评论(0编辑  收藏  举报