题意:
Solution:
我们可以把大小写分别看成0和1,这样我们就可以转化一下问题:构造一个最短的布尔函数,使得将m个01串作为变量代入这个布尔函数后,布尔函数的值都为1。这样就变成了一个加权覆盖子集的最小覆盖问题。
因为布尔函数是由一堆“或”连起来的“和”,所以我们的一个浅显的想法就是枚举子集,然后再枚举选那些子集,子集数量是 35=243 3 5 = 243 ,总复杂度 2243 2 243 ,显然我们已经爆炸了,所以说我们需要优化:
假设a,b,c都是布尔函数中的”和”,那么:
1.如果满足a条件的01串是满足b条件的01串的子集,那么除去a
2.如果a=xc,b=Xc,那么a,b都可以被c替换
这样的话我们的情况数就会减少很多,只剩下最后统计答案的复杂度
但是2^32还是会T,怎么办呢?
我们可以尝试剪枝:
我们用cover[i]表示当一个“和”的覆盖情况为i时,它可以满足哪些01串
我们用tot[i]表示[i,S-1]这段区间cover的或
然后我们进行dfs,每次先把当前状态和tot[i]取或,判断是否全部覆盖即可
具体细节请看代码。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=(1<<10);
bool can[N+10],vis[N+10];
char s[10];
int n,T,m,len[N+10],cover[N+10],tot[N+10],pos[N+10],lsh[N+10],cnt,ans,S;
void dfs(int x,int cov,int le)
{
//cout<<x<<" "<<cov<<" "<<le<<" "<<tot[x]<<" "<<cnt<<" "<<S-1<<endl;
if (le>=ans) return;
if ((cov|tot[x])<(S-1)) return;
if (cov==S-1) {ans=le;return;}
if (x==cnt) return;
dfs(x+1,cov,le);
//cout<<x<<" "<<cover[lsh[x]]<<endl;
if ((cov|cover[lsh[x]])>cov) dfs(x+1,cov|cover[lsh[x]],le+len[lsh[x]]);
}
int main()
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
for (int i=0;i<N;i++)
{
can[i]=1;
int x=i;
for (int j=0;j<5;j++)
{
if (x%4==3) can[i]=0;
if (x%4) len[i]++;
x>>=2;
}
}
scanf("%d",&T);
while (T--)
{
cnt=0;
memset(cover,0,sizeof(cover));
memset(tot,0,sizeof(tot));
memset(pos,0,sizeof(pos));
memset(vis,0,sizeof(vis));
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%s",s+1);
int x=0;
for (int j=1;j<=n;j++)
{
x<<=2;
x+=1;
if (s[j]>='a') x++;
}
// cout<<x<<endl;
vis[x]=1;
}
S=1<<n+n;
for (int i=0;i<S;i++) if (vis[i]) pos[i]=cnt++;
if (cnt==0||cnt==(1<<n)) {
printf("%d\n",0);continue;}
for (int i=0;i<S;i++)
{
if (!can[i]) continue;
bool gt=1;
for (int j=0;j<S;j++)
{
if (!can[j]) continue;
if ((i&j)==i&&len[j]==n)
{
if (vis[j]) cover[i]|=(1<<pos[j]);else gt=0;}
if ((i&j)==j&&i!=j) {
if (cover[j]) gt=0;}
}
if (!gt) cover[i]=0;
}
int SS=1<<cnt;
cnt=0;
for (int i=0;i<S;i++) if (cover[i]) lsh[cnt++]=i;
for (int i=cnt-1;i>=0;i--) tot[i]=(tot[i+1]|cover[lsh[i]]);
S=SS;
ans=1e9;
dfs(0,0,0);
printf("%d\n",ans);
}
}