UVA 1252 Twenty Question (集合DP,时间优化)
用d(s,a)表示当前状体,集合s表示已询问的特征,集合a表示已询问的特征里面属于目标物w的特征,则集合a一定位s的子集。
每一次询问对应着状态的转移,则可得到状态转移方程:
d(s,a)=min(d(s,a),max(d(s+k,a+k),d(s+k,a))+1) ,k表示询问的特征。
当有且仅有一个物体满足a集合中的所有特征,而不满足{s}-{a}中的所有特征,那么此时d(s,a)=0。
有二项式定理可以得知,状态数有O(3m)个,每次状态转移有m种策略。所以对于dp的时间复杂度是O(m*3m)。
为了避免在每次状态转移时判断满足条件的物体数,我们可以通过预处理,将每个d(s,a)状态下的满足a集合中的所有特征,而不满足{s}-{a}中的所有特征的物体数求出,记录在cnt(s,a)中,时间复杂度是O(n*2m),对于dp的时间复杂度,这可以忽略不计。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
int m,n;
int d[1<<11][1<<11];
int cnt[1<<11][1<<11];
int identity[130];
void init(void)
{
memset(cnt,0,sizeof(cnt));
memset(d,0x3f,sizeof(d));
for(int i=0;i<(1<<m);i++)
for(int j=0;j<n;j++)
cnt[i][i&identity[j]]++;
}
int dp(int s,int a)
{
if(d[s][a]!=INF)return d[s][a];
if(cnt[s][a]==1)return d[s][a]=0;
if(cnt[s][a]==2)return d[s][a]=1;
for(int i=0;i<m;i++)
if((s&(1<<i))==0)
if(cnt[s|(1<<i)][a|(1<<i)]>=1&&cnt[s|(1<<i)][a]>=1)
d[s][a]=min(d[s][a],max(dp(s|(1<<i),a|(1<<i)),dp(s|(1<<i),a))+1);
return d[s][a];
}
void solve(void)
{
init();
printf("%d\n",dp(0,0));
}
int main(void)
{
// freopen("out.txt","w",stdout);
while(~scanf("%d%d",&m,&n)&&m)
{
memset(identity,0,sizeof(identity));
string s;
for(int i=0;i<n;i++)
{
cin>>s;
for(int j=0;j<m;j++)
if(s[j]=='1')identity[i]+=1<<j;
}
solve();
}
// fclose(stdout);
}