记忆化搜索,状压DP(20个问题,uva 1252)

来源:互联网 发布:黑手党知乎 编辑:程序博客网 时间:2024/06/05 19:36

要学会找到“决策”和“状态”。

在这题中,“决策”无非就是问哪个问题,而问过的问题就不能再问了,因此需要已问过哪些问题的“状态”。每次询问,无非得到两个结果,是或不是,所以还需要“状态”:问过的问题中,那些得到了肯定的回答。问问题的顺序不会影响结果,因此需要记忆化搜索。


状态转移方程: 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 0X3F3F3F3Fusing 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;}


0 0
原创粉丝点击