要学会找到“决策”和“状态”。
在这题中,“决策”无非就是问哪个问题,而问过的问题就不能再问了,因此需要已问过哪些问题的“状态”。每次询问,无非得到两个结果,是或不是,所以还需要“状态”:问过的问题中,那些得到了肯定的回答。问问题的顺序不会影响结果,因此需要记忆化搜索。
状态转移方程: ans=min(ans,max(dp(s|(1<<i),a),dp(s|(1<<i),a|(1<<i)))+1);
题目要求找最少问几次保证问出。
保证问出:显然有的物品问一次就知道了,而有的可能要问很多次,为了保证任何物品都能问出,所以要取最大值。观察状态转移方程,每问一次,结果无非是“是”或“否”,无论是哪种结果,都一定对应着一个物品,为了保证他们都能问出来,所以选大的那个。
最少问几次:对于任何物品,有些问题是没必要问的,所以要取最小值。观察状态转移方程,对于单次询问,你问啥都是OK的,反正最终你一定能问到答案,关键是你的问题是否有必要问,我们在众多问法中选择一个次数最小的问法一定是最优的。
代码
#include<bits/stdc++.h>
#define maxn 150
#define INF 0X3F3F3F3F
using namespace std;
int m,n;
int tx[maxn];
int d[1<<11][1<<11];
int cnt[1<<11][1<<11];
char str[maxn];
int dp(int s,int a)
{
if(cnt[s][a]<0)
{
int CNT=0;
for(int i=0;i<n;i++)
if((tx[i]&s)==a) CNT++;
cnt[s][a]=CNT;
}
int& ans=d[s][a];
if(ans>=0) return ans;
if(cnt[s][a]==1) ans=0;
else
{
ans=INF;
for(int i=0;i<m;i++)
if(((1<<i)&s)==0)
ans=min(ans,max(dp(s|(1<<i),a),dp(s|(1<<i),a|(1<<i)))+1);
}
return ans;
}
int main()
{
while(scanf("%d %d",&m,&n),m+n)
{
memset(d,-1,sizeof(d));
memset(tx,0,sizeof(tx));
memset(cnt,-1,sizeof(cnt));
for(int i=0;i<n;i++)
{
scanf("%s",str);
for(int j=0;j<m;j++)
if(str[j]=='1')
tx[i]|=1<<j;
}
printf("%d\n",dp(0,0));
}
return 0;
}