状压dp

在这里插入图片描述
因为行数很小,我们可以一列一列的考虑。对于每一列的方法要受到前一列的影响,dp[i][j]表示第i列对第i+1列的影响为j的方案数,比如当n=5,j的二进制为01100时,表示当前列的第三个和第四个位置已经被之前占了。我们dfs遍历当前列的每一行。

#include<bits/stdc++.h>
using namespace std;
const int maxn=10;
const int maxm=1007;
int dp[maxm+7][(1<<maxn)];//dp[i][j]表示第i列对第i+1的影响为j
int n,m;
void dfs(int l,int h,int cur,int nex)
{
  if(h==n)
  {
    dp[l+1][nex]+=dp[l][cur];
    return ;
  }
  if(((1<<h)&cur)>0) dfs(l,h+1,cur,nex);  //当前位置被占了,就直接dfs下一行
  if(((1<<h)&cur)==0) dfs(l,h+1,cur,nex|(1<<h));//横着放
  if(h+1<n&&(((1<<h)&cur)==0)&&(((1<<(h+1))&cur)==0)) dfs(l,h+2,cur,nex);//竖着放 
  return;
}
int main()
{
  freopen("in.txt","r",stdin);
  freopen("out.txt","w",stdout);
  scanf("%d%d",&n,&m);
  dp[1][0]=1; 
  for(int i=1;i<=m;i++)
  for(int j=0;j<(1<<n);j++)
  {
       if(dp[i][j]){
       dfs(i,0,j,0);
       }
  }
  printf("%d\n",dp[m+1][0]);
  return 0;
}

在这里插入图片描述
dp[i][j]表示经过状态j后在第i个人手中的最小代价。对于状态j,假设总共有5个人,二进制01100表示经过第3和第4个人的状态。

#include<bits/stdc++.h>
using namespace std;
int n;
int a[27][27];
int dp[20][1<<16];//dp[i][j]表示经过状态j后在i的最小代价
int main()
{
     freopen("in.txt","r",stdin);
     freopen("out.txt","w",stdout);
     scanf("%d",&n);
     for(int i=0;i<n;i++)
     for(int j=0;j<n;j++)
     scanf("%d",&a[i][j]);
     memset(dp,0x3f,sizeof(dp));
     for(int i=0;i<n;i++)
     dp[i][1<<i]=0;
     for(int i=0;i<(1<<n);i++)
     {
       for(int j=0;j<n;j++)
       {
         if((1<<j)&i)
         {
         for(int k=0;k<n;k++)
         {
           if(!((1<<k)&i))
           {
             dp[k][i|(1<<k)]=min(dp[k][i|(1<<k)],dp[j][i]+a[j][k]);
           }
         } 
         }
        }
     }
     int ans=dp[0][(1<<n)-1];
     for(int i=1;i<=n;i++)
     ans=min(ans,dp[i][(1<<n)-1]);
     printf("%d\n",ans);
     return 0;
}

在这里插入图片描述
dp[i]表示当前选择的字符串的子序列编号状态压缩为i,对于每个状态,再考虑这个状态的字符中是否可以由回文串和剩余串移除。

#include<bits/stdc++.h>
using namespace std;
const int maxn=20;
const int inf=0x3f3f;
string s;
int dp[1<<maxn];
bool judge(int cur)
{
  string s2="";
  int pos=0;
  while(cur)
  {
    if(cur&1)
    {
      s2+=s[pos];
    }
    cur>>=1;
    pos++;
  }
  for(int i=0,j=s2.size()-1;i<j;i++,j--)
  {
    if(s2[i]!=s2[j]) return false;
  }
  return true;
}
int main()
{
  freopen("in.txt","r",stdin);
  freopen("out.txt","w",stdout);
  cin>>s;
  int len=s.length();
  for(int i=1;i<(1<<len);i++)
  {
    dp[i]=judge(i)?1:inf;
    for(int j=i;j;j=(j-1)&i)
    {
      dp[i]=min(dp[i],dp[j]+dp[j^i]);
    }
  }
  printf("%d",dp[(1<<len)-1]);
  return 0;
}

在这里插入图片描述
对于每一行,首先用一个数组存该行所有可选的列。然后一行一行的考虑,对于每一行枚举所有选择的,去掉当前选择中选到当前行中不合法的列或者选到了相邻列的,然后遍历上一行选择,如果上一行的选择与这一行起冲突则跳过,否则更新当前行。

自己敲的代码因为上课着急走忘了保存,不想再敲了,放上别人的代码:

#include<bits/stdc++.h>
 
using namespace std;
 
const int MAX_N = 20;
const int MAX_M = 20;
int state[MAX_N + 1];
int dp[MAX_N + 1][1 << MAX_M];
 
bool not_intersect(int now, int prev) {
    return (now & prev) == 0;
}
 
bool fit(int now, int flag) {
    return (now | flag) == flag;
}
bool ok(int x) {
    // 行内自己是否相交
    return (x & (x >> 1)) == 0;      //说明没有相邻的点
}
int count(int now) {
    int s = 0;  // 统计 now 的二进制形式中有多少个 1
    while (now) {
        s += (now & 1);  // 判断 now 二进制的最后一位是否为 1,如果是则累加
        now >>= 1;  // now 右移一位
    }
    return s;
}
 
int main() {
    int n, m;
    cin >> n >> m;
    // 初始化所有数组
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < m; ++j) {
            int flag;
            cin >> flag;
            state[i] |= (1 << j) * flag;  // 将 (i,j) 格子的状态放入 state[i] 中,state[i] 表示第 i 行的可选格子组成的集合
        }
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < (1 << m); ++j) {  // 枚举当前行的状态
            if (!ok(j) || !fit(j, state[i])) {  // 如果当前行状态不合法则不执行后面的枚举
                continue;
            }
            int cnt = count(j);  // 统计当前行一共选了多少个格子
            for (int k = 0; k < (1 << m); ++k) {
                if (ok(k) && fit(k, state[i - 1]) && not_intersect(j, k)) {  // 判断前一行是否合法和当前行和前一行的方案是否冲突
                    dp[i][j] = max(dp[i][j], dp[i - 1][k] + cnt);  // 更新当前行、当前状态的最优解
                }
            }
        }
    }
    int ans = 0;  // 保存最终答案
    for (int i = 0; i < (1 << m); ++i) {
        ans = max(ans, dp[n][i]);  
    }
    cout << ans << endl;
    return 0;
}
 
 

在这里插入图片描述
将每一包糖果的状态进行压缩。

#include<bits/stdc++.h>
using namespace std;
const int maxn=107;
const int maxm=27;
int n,m,k;
int t;
int d;
int dp[1<<20];
int a[maxn];
int main()
{
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
    {
        d=0;
        for(int j=1;j<=k;j++)
        {
            scanf("%d",&t);
            d=(1<<(t-1))|d;
        }
        a[i]=d;
        dp[d]=1;
    }
    for(int i=1;i<=n;i++)
    for(int j=0;j<(1<<m);j++)
    {
        if(!dp[j]) continue;
        if(!dp[j|a[i]]) dp[j|a[i]]=dp[j]+1;
        else dp[j|a[i]]=min(dp[j|a[i]],dp[j]+1);
    }
    printf("%d\n",dp[(1<<m)-1]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值