033动态规划之状态压缩DP——算法备赛

状态压缩DP

状态压缩是DP的一个技巧,一般应用于集合问题,当DP的状态是集合时,把集合的组合用一个二进制数表示,这个二进制数的0/1组合表示集合中元素的有无。

状态压缩DP的背景是集合状态,且集合一般可用二进制数表示,所以又称"集合DP"。

每个元音包含偶数次的最长子串

问题描述

给你一个字符串 s ,请你返回满足以下条件的最长子字符串的长度:每个元音字母,即'a','e','i','o','u',在子字符串中都恰好出现了偶数次。

原题链接

思路分析

将字符串中 5 个元音字母出现次数的奇偶性定义为一种状态,一共有 32(25)种状态,可使用一个整数代表该状态,第0位表示a的奇偶性,第1位表示e出现的奇偶性...依次类推。仅有状态为0的字符串符合题目要求。

定义状态的目的是遍历s子串的右边界 时不用再二层遍历寻找左边界。如果子串[0,i]状态为st1,与子串[0,j]的状态为st2,那子串[i+1,j]的状态就是st1^st2。只有当st1==st2时,子串[i+1,j]的状态才是0。两者是充分必要的关系。

对于每次枚举的右边界,找到最小的与其状态相同的左边界。统计的历史最大值就是答案。

代码

cpp 复制代码
int findTheLongestSubstring(string s) {
    vector<int> pre(32,INT_MAX);  //pre[i] 存储状态为i的最小左边界,初始时为INT_MIN
    pre[0]=-1;
    const int N=s.size();
    int cur=0;
    int ans=0;
    for(int i=0;i<N;++i){
        switch(s[i]){
            case 'a':cur^=1;break;
            case 'e':cur^=2;break;
            case 'i':cur^=4;break;
            case 'o':cur^=8;break;
            case 'u':cur^=16;break;
            default:break;
        }
        if(pre[cur]==INT_MAX) pre[cur]=i;  //未初始化,现在初始化
        else ans=max(ans,i-pre[cur]);
    }
    return ans;
}

Hamilton(旅行商)问题

问题描述

给定一个有权无向图,包括 n 个点,标记为 0~n-1,以及连接 n 个点的边,求从起点 0到终点 n-1的最短路径。要求必须经过所有点,且只经过一次。1<=n<=20。

输入:第一行输入 n,接下来 n 行,每行输入 n 个整数,其中第 i 行第 j 个整数表示点 i 到点 j 的边的距离,记为 a[i,j]。a[x,x]=0,a[x,y]>=0

输出最短距离。

思路

定义dp,设 s 为图的一个子图,用dp[s][j]表示集合 s 内的最短 Hamilton路径,即从起点0出发经过S所有点到 j 的最短路径。

如何从 S-j (集合s中去掉j)递推到S ?

设 k 为 S-j 中的一个点,把 0~j 的路径分为 0-1-...-k和 k-(k+1)-...-j 以k为变量枚举 s~j 中所有点,找出最小路径

dp[s][j]=min{dp[s-j][k]+dis[k,j]}, k 属于 s-j。

如何表示集合s

用一个二进制数表示S,S中的每一为0/1表示图中点的有无。本题n最多20,用int数据(32位)够用。

代码

cpp 复制代码
int n,dp[1<<20][21];
int dist[21][21];
int main(){
    memset(dp,0x7f,sizeof(dp));  //初始化最大值
    //用容器可表示为 vector<vector<int>>dp(1<<20,vector<int>(21,INT_MAX));
    cin>>n;
    for(int i=0;i<n;i++){  //输入图
		for(int j=0;j<n;j++){
			cin>>dist[i][j];
        }
    }
    dp[0][0]=0;
    for(int s=1;s<(1<<n);i++){
        for(int j=0;j<n;j++){ 
            if((s>>j)&1) //判断第j位是否为1 即判断子图s中是否有j  (1)  没有直接下一轮循环。
                for(int k=0;k<n;k++){  //枚举每个k 取最小值
                    int st=s^(1<<j)//s^(1<<j) 用于将s中第j为置0,此时st为不含j的子图
					if(st>>k&1) //if(((s>>j)&1)&&((s^(1<<j))>>k&1)) 把(1)合并进来这样写也行,但效率低些
                        dp[s][j]=min(dp[s][j],dp[st][k]+dist[k][j]);
                }  
        }
    }
    cout<<dp[(1<<n)-1][n-1];
    return 0;
}
相关推荐
小七在进步16 分钟前
类和对象(五)
java·开发语言·算法
就叫年华吧丶25 分钟前
长文档点目录定位总是不准?一个 content-visibility + 平滑滚动引发的连环坑(Vue3 实战)
前端·javascript·算法·vue
FPGA信号处理32 分钟前
【信号检测与估计】第四节课:非高斯噪声下的 BLUE、极大似然与 EM 算法
人工智能·算法·机器学习
.道阻且长.1 小时前
C++ 11:可变参数模板
前端·c++·算法
Doubbbbbbble云3 小时前
基于贪心算法的区间覆盖问题求解策略4
算法·贪心算法
不会就选b9 小时前
算法日常・每日刷题--<贪心>25
数据结构·算法
千里码aicood13 小时前
基于CART算法的图书分类系统设计与实现
数据库·算法·分类
做cv的小昊13 小时前
【World Model】π0.5:a Vision-Language-Action Model with Open-World Generalization
人工智能·算法·机器学习·大模型·多模态·vla·世界模型
لا معنى له14 小时前
World-In-World: World Models in a Closed-Loop World
算法