状态压缩DP 题目小节 (一)

最近被状态压缩DP虐得不行,今天终于决定正视自己的弱项,好好把DP练习一下,把今天做的几道状态压缩DP总结一下,一定要想办法摆脱DP弱菜这个标签!!!

http://poj.org/problem?id=3254

poj 3254 :

应该是最基础的状态压缩DP了吧,设dp[i][flag]表示第i行状态为flag时的排放总数,预处理一下dp[1][flag],对于dp[i][flag](i>=2),则dp[i][flag]=dp[i][flag]+dp[i-1][pre]当且仅当pre满足以下几个条件:

1:flag和pre都不含有相邻的1(二进制)

2:flag和pre分别满足第i行和第i-1行的约束条件。

3:flag和pre在同一位上不能同时为1(二进制)。

还是挺简单的,用位运算可以简单实现。代码如下:


#include <iostream>
#include <string.h>
#include <stdio.h>
#include <vector>
#define mod 100000000
using namespace std;
vector<int> t;
int check(int x)
{
    int i;
    for(i=0;i<=10;i++)
    {
        int tmp=(1<<i)+(1<<(i+1));
        if((x&tmp)==tmp)
        return 0;
    }
    return 1;
}
void init()
{
    int i;
    t.push_back(0);
    for(i=1;i<(1<<12);i++)
    {
        if(check(i))
        t.push_back(i);
    }
}
int dp[13][400];
int num[13];
int main()
{
    //freopen("dd.txt","r",stdin);
    init();
    int n,m,i,j;
    scanf("%d%d",&n,&m);
    memset(dp,0,sizeof(dp));
    for(i=1;i<=n;i++)
    {
        int tmp=0;
        for(j=1;j<=m;j++)
        {
            int x;
            scanf("%d",&x);
            tmp=tmp*2+x;
        }
        num[i]=tmp;
    }
    int limit=1<<m,len=t.size();
    long long ans=0;
    for(i=0;i<len;i++)
    {
        if(t[i]>=limit)
        break;
        int now=t[i];
        if((now|num[1])==num[1])
        {
            dp[1][i]=1;
        }
    }
    for(i=2;i<=n;i++)
    {
        for(j=0;j<len;j++)
        {
            if(t[j]>=limit)
            break;
            int now=t[j],s;
            if((now|num[i])==num[i])
            {
                for(s=0;s<len;s++)
                {
                    if(t[s]>=limit)
                    break;
                    int pre=t[s];
                    if((num[i-1]|pre)==num[i-1]&&(pre&now)==0)
                    {
                        dp[i][j]=(dp[i][j]+dp[i-1][s])%mod;
                    }
                }
            }
        }
    }
    for(i=0;i<len;i++)
    {
        if(t[i]>=limit)
        break;
        ans=(ans+dp[n][i])%mod;
    }
    printf("%I64d\n",ans);
    retur
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我们来看一个具体的例子。假设有一个长度为 n 的数组 A,其中每个元素都是 0 或 1,现在需要求出所有长度为 k 的子串中,元素为 1 的个数的最小值。 传统的动态规划方法需要使用二维数组来记录状态,时间复杂度为 O(nk),空间复杂度为 O(nk)。而使用状态压缩dp,我们可以将状态压缩为一个长度为 n 的二进制数 i,其中第 j 位为 1 表示 A[j] 在当前子串中出现了一次或多次,为 0 则表示没有出现。因此,我们只需要使用一个一维数组 f 来记录当前状态的最小值即可。 具体实现如下: ```python def min_ones_in_k_substrings(A, k): n = len(A) f = [float('inf')] * (1 << n) f[0] = 0 for i in range(n): for j in range(1 << i): if bin(j).count('1') == k: ones = bin(j & ((1 << i) - 1)).count('1') + A[i] f[j] = min(f[j], f[j & ~(1 << i)] + ones) return f[(1 << n) - 1] ``` 其中,f[i] 表示状态为 i 时的最小值,初始化为正无穷。在状态转移时,我们枚举当前状态的所有子集 j,如果 j 中的元素个数等于 k,则计算 j 中包含的所有元素为 1 的个数 ones,然后更新 f[j] 的值为 f[j] 和 f[j - {i}] + ones 中的较小值。其中,j - {i} 表示将 j 中的第 i 位(即 A[i] 对应的位置)置为 0。 最终,我们返回状态为全集时的最小值 f[(1 << n) - 1] 即可。由于状态总数为 2^n,因此时间复杂度为 O(n^22^n),空间复杂度为 O(2^n)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值