標籤:dp
集合上的dp,看小紫書想了好久~
有n個物體,m個特徵,最少詢問多少次能保證猜到是哪個問題。
首先在讀入的時候將每個物體轉化為一個二進位整數,用一個集合s來表示已經詢問了哪些特徵。
用d(s,a)來表示詢問了集合s的特徵,有集合a個特徵已經具備,還需要問多少個特徵的最小值。a一定為s的子集。
則狀態方程為 d(s,a) = min{ max(d(s+{k},a+{k}),(s+{k},a) )+1 ,| {k}?s}。邊界條件為,若只有一個物體具有a的特徵,且不具有s-a的特徵,則d(s,a)=0;
#include<cstdio>#include<cstring>#include<algorithm>#define mem(name,value) memset(name,value,sizeof(name))using namespace std;const int maxn=130;const int maxm=11;const int inf=0x3f3f3f3f;int d[1<<maxm][1<<maxm];int cnt[1<<maxm][1<<maxm];int p[maxn],n,m;char t[maxm+3];void init(){ mem(cnt,0); mem(d,-1); for(int i=0;i<n;i++){ scanf("%s",t); int la= strlen(t); for(int j=0;j<la;j++) p[i] = (p[i]<<1)+t[j]-48; //物體轉換為二進位整數 } for(int i=0;i<(1<<m);i++) //預先處理每個狀態有多少個物體滿足 for(int j=0;j<n;j++) cnt[i][i & p[j]]++; //每個物體在狀態i下滿足的狀態(就是詢問了集合i中有哪些特徵滿足)}int dp(int s0,int s1){ if(d[s0][s1]!=-1) return d[s0][s1]; if(cnt[s0][s1]<=1) return d[s0][s1]=0; //滿足這個狀態的只有一個物體,則傳回值為0 d[s0][s1]=inf; for(int i=0;i<m;i++){ if(s0 & (1<<i)) continue; //只能詢問還沒有詢問過的 int& ans = d[s0][s1]; int t0 = s0^(1<<i); ans = min(ans,max(dp(t0,s1),dp(t0,s1^(1<<i)))+1); //因為詢問了第i個特徵,對不同物體有可能具有或者不具有,而又要保證能夠得到是哪個物體,則應取max; //而通過不同的狀態來達到目前狀態時,則應該取能達到目前狀態的min } return d[s0][s1];}int main(){ // freopen("in.txt","r",stdin); while(~scanf("%d%d",&m,&n) && m && n){ init(); int ans = dp(0,0); //還沒有詢問任何特徵。 printf("%d\n",ans); } return 0;}
uva 1252 Twenty Questions 狀壓dp