状态压缩--动态规划
状态压缩也就是把多个状态都转译成一个状态,由于题目的题意就是需要一步一步递推也就是dp,但是常规的dp只能计算一个状态,无法满足多个状态,所以可以使用状态压缩.
将这多个状态划分为二进制形式:设有$m$个状态,那么所有的可能状态为 $2^m$ ,如果有4个状态,$0010$ 表示只满足了第二个状态,依次类推
但是可以看出,输出的不可能存在一个都不满足的状态,所以计算时状态总数为 $2^m-1$
状态合并: 使用 $ | $,将两个状态的1合并
题目描述
糖果店的老板一共有 $M$ 种口味的糖果出售。为了方便描述,我们将 $M$ 种口味编号 $1$ ∼ $M$。
小明希望能品尝到所有口味的糖果。遗憾的是老板并不单独出售糖果,而是 $K$ 颗一包整包出售。
幸好糖果包装上注明了其中 $K$ 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。
给定 $N$ 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖果。
## 输入格式
第一行包含三个整数 $N$、$M$ 和 $K$。
接下来 $N$ 行每行 $K$ 这整数 $T_1,T_2, \cdots ,T_K$,代表一包糖果的口味。
## 输出格式
一个整数表示答案。如果小明无法品尝所有口味,输出 $-1$。
样例 #1
样例输入 #1
```
6 5 3
1 1 2
1 2 3
1 1 3
2 3 5
5 4 2
5 1 2
```
样例输出 #1
```
2
```
把输出的每个包装里面都计算出所含的糖果状态,然后进行dp即可,时间复杂度 $O(n*2^m)$
#include<bits/stdc++.h> using namespace std; const int N=500; int n,m,k; int f[1<<25],candy[1<<25]; bool vis[1<<25]; int main() { cin>>n>>m>>k; for(int i=1;i<=n;i++){ int num=0; for(int j=1;j<=k;j++){ cin>>num; candy[i]=candy[i]|1<<(num-1); vis[candy[i]]=true,f[candy[i]]=1; } } for(int i=1;i<=n;i++) for(int j=1;j<1<<(m+1);j++) if(vis[j]){ if(!vis[j|candy[i]]) f[j|candy[i]]=f[j]+1; else f[j|candy[i]]=min(f[j|candy[i]],f[j]+1); vis[j|candy[i]]=true; } if(!vis[(1<<m)-1]) cout<<-1; else cout<<f[(1<<m)-1]<<endl; return 0; }
1 //https://www.luogu.com.cn/problem/P1433 2 3 //将所有的奶酪看做成状态,0为未被吃,1为被吃 4 //枚举所有的状态,f[i][j]表示,在吃到第i个奶酪下,没有吃到j奶酪的距离 5 //用i对所有的j全都更新一边最短距离即可 6 #include<bits/stdc++.h> 7 using namespace std; 8 const int N=5e4+10; 9 int n,m; 10 double f[25][N],dis[500][500]; 11 double x[N],y[N],res=2e9; 12 double distance_(double x1,double x2,double y1,double y2) 13 { 14 return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); 15 } 16 int main() 17 { 18 cin>>n; 19 for(int i=1;i<=n;i++) cin>>x[i]>>y[i]; 20 x[0]=y[0]=0; 21 for(int i=0;i<=n;i++) 22 for(int j=i+1;j<=n;j++) dis[j][i]=dis[i][j]=distance_(x[i],x[j],y[i],y[j]); 23 memset(f,0x7f,sizeof f); 24 for(int i=1;i<=n;i++) f[i][1<<(i-1)]=dis[0][i]; 25 for(int k=1;k<(1<<n);k++){ 26 for(int i=1;i<=n;i++){ 27 if(!(k&1<<(i-1))) continue; 28 for(int j=1;j<=n;j++){ 29 if(!(k&1<<(j-1))||i==j) continue; 30 f[i][k]=min(f[i][k],f[j][k-(1<<(i-1))]+dis[j][i]); 31 } 32 } 33 } 34 for(int i=1;i<=n;i++) res=min(res,f[i][(1<<n)-1]); 35 printf("%.2lf",res); 36 return 0; 37 }
//https://www.luogu.com.cn/problem/P1896 //压状dp //由于时间复杂度达到了指数级复杂度,所以搜索不能进行 //考虑动态规划,dp i,j,k表示 第i行,使用了j个国王,此时的状态是k // 对于第i行,若要求此时符合条件,有: 第i行中的棋子,两两互不相邻,即check(a|b)=true // 对于第i-1行,有与第i行的国王不相邻,所以使用&运算即可 #include<bits/stdc++.h> using namespace std; const int N=12,M=1<<10,K=110; int n,m,cnt[M],dp[N][M][K]; vector<int>state,head[M];//把状态存入state中 int count(int x)//计算1的个数 { int res=0; for(int i=0;i<n;i++) if((x>>i&1)==1) res++; return res; } bool check(int x) //检查相邻两位是否连续为1 { for(int i=0;i<n;i++) if((x>>i&1)&&(x>>i+1&1)) return false; return true; } int main() { cin>>n>>m; for(int i=0;i<1<<n;i++) if(check(i)) state.push_back(i),cnt[i]=count(i); for(int i=0;i<state.size();i++) for(int j=0;j<state.size();j++){ int a=state[i],b=state[j]; if(!(a&b)&&check(a|b)) head[i].push_back(j); } dp[0][0][0]=1; for(int i=1;i<=n+1;i++) for(int j=0;j<=m;j++) for(int a=0;a<state.size();a++) for(int b:head[a]){ int c=cnt[state[a]]; if(j>=c) dp[i][j][a]+=dp[i-1][j-c][b]; } cout<<dp[n+1][m][0]; return 0; }