第五章 动态规划 (二)(数位统计DP,状态压缩DP,树形DP)

文章讲述了如何运用状态压缩动态规划方法解决计数问题,包括区间内特定位数计1的次数、蒙德里安梦想问题(固定横向摆放计算竖向方式)、最短汉明顿路径问题以及无上司舞会的树形DP问题。作者详细展示了代码示例以解释这些复杂问题的解法。
摘要由CSDN通过智能技术生成

一、计数问题 (数位统计DP)

求一个区间所有数的各位中,中1~9各自出现的次数。

如果要求1-xxxxxxx中第四位中1出现的次数,思想是分情况讨论(划分集合):

假设数为:1 <= xxx1xxx<=abcdefg

1、若前三位xxx== (0,abc-1) ,yyy=0~999 都行   :abc*1000

2、若xxx == abc  

    2.1 当 d<1  的时候: 0

    2.2 当 d==1 的时候:0-efg ,efg+1种

    2.3 当 d>1的时候,0-999, 1000种

二、状态压缩DP 291、蒙德里安的梦想:

思路:当所有的横向摆放的情况固定之后,竖向也已经固定,只要求出所有横向摆放的方式数。

状态表示:f[ i , j ]表示第i列的状态为j(从i-1列捅过来的)的时候的方案数;

界限是:从0到m,为什么要让f[0,0]=1,因为我们的思路是,j

关于为什么要将f[0,0]=1;要注意:我们要放的是从0~m-1列,而只有将其设置为1,我们在m==2的时候,才能无论j是什么状态,都可以成功加1。

为什么要计算f(m,0):

#include<bits/stdc++.h>
using namespace std;
const int N=12,M=1<<12;
long long  f[N][M];
bool st[N];//用于判断i处于状态j的时候,i-1列的状态是否满足
int main()
{
    int n,m;
    while(cin>>n>>m ,n||m)
    {
        memset(f,0,sizeof(f));
        for(int i=0;i<1<<n;i++)
        {
            st[i]=true;
            int cnt=0;
            for(int j=0;j<n;j++)
            {
                if(i>>j&1)
                {
                    if(cnt&1)st[i]=false,cnt=0;

                }
                else cnt++;
            }
            //最后肯状态的后几个数都是0,需要进行判断
            if(cnt&1)st[i]=false;
        }
        f[0][0]=1;
        for(int i=1;i<=m;i++)
        {
            for(int j=0;j<1<<n;j++)
            {
                for(int k=0;k<1<<n;k++)
                {
                    if((j&k)==0&&st[j|k])
                    {
                        f[i][j]+=f[i-1][k];
                    }
                }
            }
        }
        cout<<f[m][0]<<endl;
    }
    return 0;
}

三、最短Hamilton路径(状态压缩DP)

不重不漏走过所有点的最短路径。

状态表示:i为从0到j走过的所有点的二进制。f值为其最小的路径长度。

状态划分,目前路径的倒数第二个数是谁。

#include<bits/stdc++.h>
using namespace std;
//91. 最短Hamilton路径
const int N=20,M=1<<N;
int n;
int w[N][N];
int f[M][N];
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            cin>>w[i][j];
        }
    }
    memset(f,0x3f,sizeof(f));
    //起点为0,到0的距离就是0
    f[1][0]=0;
    for(int i=0;i<1<<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(i>>j&1)//这个点可以走到
            {
                //集合划分:倒数第二个点是谁
                for(int k=0;k<n;k++)
                {
                    if((i-(1<<j))>>k&1)
                        f[i][j]=min(f[i][j],f[i-(1<<j)][k]+w[k][j]);
                }
            }
        }
    }
    cout<<f[(1<<n)-1][n-1]<<endl;


}

四、树形DP  285 没有上司的舞会

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值