状态压缩--动态规划

状态压缩也就是把多个状态都转译成一个状态,由于题目的题意就是需要一步一步递推也就是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;
}

 

posted @ 2023-09-02 17:36  o-Sakurajimamai-o  阅读(51)  评论(0编辑  收藏  举报
-- --