CF1117F Crisp String

没有用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;
}
原文地址:https://www.cnblogs.com/jasony/p/13738025.html