算法-DP

本文详细探讨了动态规划(DP)的各种问题类型,包括状压DP、一维DP、青蛙跳台阶问题和背包问题。文章通过具体题目,如判断奇偶性质、连续1的判断、字符串解码、买票问题等,讲解了DP的应用和解题思路,并提供了相应的代码实现。
摘要由CSDN通过智能技术生成

状压DP问题

1.2021-4-15 用异或性质判断 奇偶性质

题目:

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

示例1

输入

s = "eleetminicoworoep"

输出

13

解释

最长子字符串是 "leetminicowor" ,它包含 e,i,o 各 2 个,以及 0 个 a,u 。

思路

非常巧妙的一道题。

用了异或的性质:00001^00001=00000,00000^00001=00001
00010^00010=00000。
即一个二进制数 某个位如果 异或 当前位为1,其它位为0的数,那么会从1->0,从0->1。

这种性质刚好符合了奇数+1=偶数,偶数+1=奇数的性质。

如果当前题目是统计a为偶数时最长子字符串。
那么只需要通过一个dp[2]来记录dp[1]时的下标和dp[0]时的下标,每次更新一下下标差求最大值。
其status只需要用到长度为1的二进制数即可。

那么题目要求5个字符,设置status为长度为5的二进制数即可。status=00000初始。
每次和00001 00010 00100 01000 10000进行异或。

注意一个细节dp[0]=0而不是-1,这是因为00000是默认已经出现过的。

还有坐标通过i+1来存,这是因为下标差与实际长度差1。

代码:

class Solution {
   
public:
    int findTheLongestSubstring(string s) {
   
        int n=s.size();
        vector<int> dp(32,-1);
        dp[0]=0;//00000出现了一次
        int status=0;
        int ans=0;
        for(int i=0;i<n;++i){
   
            char c=s[i];
            if(c=='a')status^=(1<<0);
            else if(c=='e')status^=(1<<1);
            else if(c=='i')status^=(1<<2);
            else if(c=='o')status^=(1<<3);
            else if(c=='u')status^=(1<<4);
            if(dp[status]!=-1)ans=max(ans,i+1-dp[status]);
            else dp[status]=i+1;
            //注意status保存成i+1 完全是因为索引差和长度差11
        }
        return ans;
    }
};

2.2021-4-15 判断连续1 相邻1

POJ3254

思路

首先是可以种植的所有状态个数为SUM 判断依据为避免行相邻 i&(i<<1)==0
其次是自己行不能种植的格要空开,即cant[i]+=(1<<(N-j)) if tmp==0
某个状态是否与另一个状态在同一个位上有相同的1,即i&j!=1即可


特别需要注意的一点是int status[SUM]存放了各个状态,而1~SUM只是状态的编号


dp[i][j]为第i行第j种状态可以选择的所有方案数
dp[i][j]==dp[i-1][k1]+dp[i-1][k2]+...+dp[i-1][ksum]
首先是j&cant[i]!=1
然后dp[i-1[k:1~NUM] k&j!=1 k&cant[i-1]!=1

代码:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<vector>
#include<stdio.h>
#include<string.h>
using ll = long long;
using namespace std;

int M, N;
int SUM;
int cant[13];
int status[5000];
const int MOD = 100000000;
void Init() {
   
	SUM = 0;
	for (int i = 0; i < (1 << N); ++i) {
   
		if ((i & (i << 1)) == 0)status[++SUM] = i;
	}
}
bool Judge(int i, int j) {
   
	if (i& j)return false;
	else return true;
}

int main() {
   
	while (cin >> M >> N) {
   
		memset(cant, 0, sizeof(cant));
		memset(status, 0, sizeof(status));
		Init();
		for (int i = 1; i <= M; ++i) {
   
			for (int j = 1; j <= N; ++j) {
   
				int tmp; cin >> tmp;
				if (tmp == 0)cant[i] += (1 << (N - j));
			}
		}
		vector<vector<int> > dp(M + 1, vector<int>(SUM+1, 0));

		for (int j = 1; j <= SUM; ++j) {
   
			if (Judge(status[j], cant[1]))dp[1][j] = 1;
		}

		for (int i = 2; i <= M; ++i) {
   
			for (int j = 1; j <= SUM; ++j) {
   
				if (!Judge(status[j], cant[i]))continue;
				for (int k = 1; k <= SUM; ++k) {
   
					if (!Judge(status[k], status[j]))continue;
					if (!Judge(status[k], cant[i - 1]))continue;
					dp[i][j] = (dp[i - 1][k]+ dp[i][j])%MOD;
				}
			}
		}

		int ans = 0;
		for (int j = 1; j <= SUM; ++j) {
   
			ans = (ans+dp[M][j])%MOD;
		}
		cout << ans << endl;
	}
	return 0;
}

一维DP问题

1.2021-4-4 字符串字符结合分析

题目:

已知摩尔斯电码和字符映射关系如下:

A -> 0 

B -> 1 

C -> 10 

D -> 11 

E -> 100 

F -> 101 

G -> 110 

H -> 111 

当前我们获取到了一串01数字字符串,需要进行摩尔斯电码解码,请问共有多少种解码方法?

输入描述:

一行由0和1组成的字符串

输出描述:

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值