蓝桥杯---动态规划(1)
创始人
2024-03-08 08:22:50
0

动态规划专题(1)

  • 1.糖果(状压dp)
  • 2.调手表(状压dp)
  • 3.矩阵计数(状压dp)
  • 4.蒙德里安的梦想(状压dp模板题)
  • 5.跳跃(动态规划,搜索)
  • 5.字符排序(逆序对(难))
  • 6.子串分值和(哈希表+子串小推导)
  • 7.游园安排(最长不上升子序列dp)

1.糖果(状压dp)

方法:状态压缩+简单dp(简称:状压dp)
状压dp方法步骤:

1.预处理好状态
2.使用处理好的状态进行dp

思路:对于每袋糖果,要么选,要么不选。对于当前选择的一袋糖果,如果
这袋糖果里面有之前没有尝过的,那么选择它,如果这袋糖果所有的口味都已经尝过,那么就不要这一袋。那么只需要将当前的尝过的口味的状态表示处理,就可以用简单的01背包去完成这道题。

在这里插入图片描述

#include 
#include
#include
using namespace std;const int N=(1<<20)+10,INF=0x3f3f3f3f;int w[N];  //存放每一袋糖果的状态
int dp[N];   //dp[i]=j表示状态为i的情况下最少尝多少袋糖果int n,m,k;
int main()
{cin>>n>>m>>k;   //袋数,口味种类,每袋的数量memset(dp,-1,sizeof dp);   for(int i=1;i<=n;i++){for(int j=0;jint c;cin>>c;w[i]|=(1<<(c-1)); //运算符‘|’的规则是有1则1。//解释一下这句话.//1<for(int j=0;j<(1<if(dp[j]!=-1)  //如果当前状态存在,那么说明这个可以由这个状态去转移。{if((dp[j|w[i]])==-1) //选择第i袋糖果的状态没有存在,则说明找到了新口味,选它!dp[j|w[i]]=dp[j]+1;else      //如果选了第i袋的状态和没有选i的状态是一样的。说明(j|w[i])这个状态的最小数之前算过了,那么比较一下选这一袋和之前的袋数哪个小dp[j|w[i]]=min(dp[j]+1,dp[j|w[i]]);}else  //否则这个状态一定是第第一次出现{continue;}}}cout<

2.调手表(状压dp)

简单dp+预处理
在这里插入图片描述
在这里插入图片描述

#include
#include
#include
using namespace std;const int N=100100;int dp[N];long long int n,k,j;int main()
{cin>>n>>k;dp[0]=0;for(int i=1;idp[i]=i;if(i%k==0&&i>=k)dp[i]=i/k;}for(int i=1;ij=i*k%n;dp[j]=min(dp[j],i);}for(int i=2;i//要考虑是否走若干1,又调k绕圈到达某分钟步数最小dp[i]=min(dp[i],min(dp[(i+n-k)%n],dp[i-1]))+1;}int res=0;for(int i=1;i

3.矩阵计数(状压dp)

在这里插入图片描述

思路:搜索或者状压dp
状压dp:(1)预处理合法状态。(2)定义dp。 (3)根据dp定义求状态转移方程
这道题的dp定义:dp[i][j][k]表示前i层,第i层的状态是j,第i-1层的状态是j-1的合法方案数

#include 
#include
#include
#include
using namespace std;const int N=1<<6;
vectorstate; //存放合法状态
int dp[N][N][N];   //dp[i][j][k]表示前i层,第i层状态j,第i-1层状态时k的合理方案数量
int res=0;
int n,m;
bool check(int x)  //挑选出合法状态
{//不能有连续3个1int sum=0;  //记录1的个数while(x){if(x&1)  //‘|‘:同1则1{sum++;if(sum>=3)return false;}elsesum=0;x>>=1;}return true;
}int main()
{cin>>n>>m;//预处理状态//0表示0,1表示xfor(int i=0;i<1<if(check(i))state.push_back(i);}memset(dp,0,sizeof dp);for(int i=0;ifor(int j=0;jif((state[j]&state[k]&state[L])==0)  //表示合法状态dp[i][state[j]][state[k]]+=dp[i-1][state[k]][state[L]];}}int ans=0;for(int i=0;i

用搜索来做

#include
#include
#include
#include
using namespace std;const int N=1<<6;
int v[N][N];
int n,m;
int ans=0;bool check()  //检查当前矩阵是否合法
{for(int i=1;i<=n;i++)//考虑到可能每一行不足三个数{for(int j=1;j<=m;j++){if(j+2<=m){if(v[i][j]==1&&v[i][j+1]==1&&v[i][j+2]==1)return false;}if(i+2<=n){if(v[i][j]==1&&v[i+1][j]==1&&v[i+2][j]==1)return false;}}}return true; 
}void dfs(int x,int y)  //一个一个枚举
{if(x==n&&y==m+1){if(check())ans++;return;}if(y==m+1) //说明该行已经枚举完,下一行{y=1;  //从新的一行第一列开始x++;}v[x][y]=0;dfs(x,y+1);v[x][y]=1;dfs(x,y+1);
}int main()
{cin>>n>>m;//用搜索来做//一行一行来枚举dfs(1,1);   //从1行1列开始枚举cout<

4.蒙德里安的梦想(状压dp模板题)

5.跳跃(动态规划,搜索)

在这里插入图片描述
在这里插入图片描述
最基本的dp问题。递推即可。对于任意一格,有9种走法。实际上将题目简化一下就是对于任意一步只有两种走法。但是本质是一样的

#include 
#include
#include
using namespace std;
const int N=110;
int w[N][N];
int dp[N][N];   //dp[i][j]表示当前位于(i,j)的位置上的总价值int n,m;
int main()
{cin>>n>>m;for(int i=0;i>w[i][j];//定义dp,推出dp方程//dp[i][j]=max(以(i,j)为等腰三角形顶点,三角形内所有点都是前一步可能的点,注意这个等腰三角形和往前走的三角形是倒着的)memset(dp,-0x3f,sizeof dp);dp[0][0]=w[0][0];for(int i=0;i//每一步都有9种走法//往上走3个,往左走3个,斜着走3个for(int a=1;a<=3;a++){if(i>=a)dp[i][j]=max(dp[i][j],dp[i-a][j]);if(j>=a)dp[i][j]=max(dp[i][j],dp[i][j-a]);}if(i>=1&&j>=1)dp[i][j]=max(dp[i][j],dp[i-1][j-1]);if(i>=1&&j>=2)dp[i][j]=max(dp[i][j],dp[i-1][j-2]);if(i>=2&&j>=1)dp[i][j]=max(dp[i][j],dp[i-2][j-1]);if(!(i==0&&j==0))dp[i][j]+=w[i][j];}cout<

5.字符排序(逆序对(难))

在这里插入图片描述
在这里插入图片描述

#include
using namespace std;
int V , len , now , cnt[27] , sum[27];
int get_max(int len){return ((len - (len / 26 + 1)) * (len / 26 + 1) * (len % 26) + (26 - len % 26) * (len / 26) * (len - len / 26)) / 2;
}
bool check(int x , int n){//当前位置i放x字母后,后面的n个位置是否符合要求 memset(cnt , 0 , sizeof(cnt));//cnt记录后面n个位置放置的对应字符数量int add1 = 0 , add2 = 0;for(int j = 26 ; j >= x + 1 ; j --) add1 += sum[j];//1~i-1里边比当前插入字符大的字符数量sum[x] ++ ;//当前字符数量增加1for(int L = 1 ; L <= n ; L ++){//当前位置i放了x,看当前位置i后面剩下的n个位置能不能放好字母使得达到V的条件,这是一个预测过程 //ma保存i位置后面的n个位置能增加的最大逆序对个数 L-1-cnt[j]+num//L-1-cnt[j]是当前字符之后的比当前字符小的字符数量的最大值//num是1~pos+L-1前的比当前放置字符字典序大的字符数量int ma = -1 , pos = 0 , num = 0;for(int j = 26 ; j >= 1 ; j --){//枚举i位置后面的n个位置,每个位置放什么字母 if(L - 1 - cnt[j] + num > ma){//L - 1是算i+1到i+L的个数(不算i+L自己) // - cnt[j] 如果在i+1到当前位置i+L前有放过字母j,然后当前位置i+L也尝试放j,//那当前位置不和前面放j的位置构成逆序对 要减掉前面放j的位置个数 ma = L - 1 - cnt[j] + num;pos = j;//在L这个位置放上字母j }num += sum[j];//num是从1-i位置里大于等于j的数的个数 //因为j是从26逆着放到1的 到下一轮循环时,j--假设记为j',那要求的1-i里比j'大的数就是num //那么这个j'会和前面已经确定好的所有属于26~(j'+1)的数构成逆序对//其实最后的num是算的所有大于等于1的个数,但是没关系,它不会更新到ma里 }add2 += ma , cnt[pos] ++;//L这个位置确定下来,放pos这个字母 }if(now + add1 + add2 >= V) {//now是i这个位置以前的所有逆序对个数,//add1是i位置放进x后,x与前面所有字母构成的逆序对 //add2是i位置放进x后,后面的位置的所有数能增加的逆序对个数,分为2部分,//1、后面位置所有的数与1~i部分的数构成的逆序对个数//2、后面所有位置L,分别与从i+1~i+L-1之间的数构成的逆序对个数,然后这些逆序对的和 now += add1;//now更新i位置确定好放字母x后,从1~i位置所有逆序对个数 return true;}else {//i这个位置放x不能使逆序对个数>=V sum[x] -- ;return false;}
}
signed main()
{string ans = "";cin >> V;for(int i = 1 ; ; i ++) {if(get_max(i) >= V){//i这个长度的字符串最多能有get_max(i)个逆序对 如果get_max(i) >= V,//那么i这个长度就是我们需要的最短长度 len = i;break ;}}for(int i = 1 ; i <= len ; i ++){//按长度从左往右尝试放字母 for(int j = 1 ; j <= 26 ; j ++){//按顺序'a'-'z' 26个字母放   先用1-26代表这26个字母 if(check(j , len - i)){//现在是在第i位放j字母  看i后面的len-i个字母能否符合条件 ans += char(j + 'a' - 1);//符合条件就把j放在i这个位置 break ;//然后尝试放下一个位置 }}}cout << ans << '\n';return 0;
}

6.子串分值和(哈希表+子串小推导)

在这里插入图片描述

思路:
原始思路是枚举所有的子串,然后对每个子串进行求值,最后求和。但是这样会超时。

换个方法:枚举所有字符,累加另包含当前字符的子串数目。

#include 
#include
#include
#includeusing namespace std;const int N = 1e6 + 10,INF=0x3f3f3f3f;
int visited[1000];
long long int sum = 0;
int main()
{string s;cin >> s;memset(visited, -INF, sizeof visited);//我们要算出每个字母的贡献值累加即可//(1)当前字母如果在前面出现过,那么以最近一次出现该字母后面的一个字母为起点,字符串最后一格字符为终点。当前字母将//起点到终点分为两个部分,两个部分相乘即可for (int i = 0; i < s.length(); i++){//枚举到当前字符,以当前字符为中心将字符串分为两个部分long long int L, R;  //L表示左边的字符个数,R右边.左右相乘if (visited[s[i]] <0)  //说明该字符前面没有出现过。{L = i + 1;R = s.length() - i;}else   //否则出先过,应该从最近一次出现的后一个字符作为起始字符{L = i - visited[s[i]];R = s.length() - i;}sum += L * R;   //左右相乘visited[s[i]] = i;   //更新当前字符的位置}cout << sum << endl;return 0;
}

7.游园安排(最长不上升子序列dp)

在这里插入图片描述
在这里插入图片描述

#include 
#include
#include
using namespace std;const int N = 1e5 + 10;
int dp[N];  //dp[i]=j表示
string s[N];   //s[1]表示第一个名字
int path[N];  //存放路径
int a[N];   //存放路径int main()
{string str;cin >> str;int k = -1;for (int i = 0; i < str.length(); i++)  //预处理字符串 ,先预处理一下名字,将其放入一个string数组{if (str[i] >= 'A' && str[i] <= 'Z'){k = k + 1;s[k]+=str[i];}elses[k]+=str[i];}//初始化dpfor (int i = 0; i <= k; i++)  //k个名字dp[i] = 1;memset(path, -1, sizeof path);memset(a, -1, sizeof a);int start = 0;int res = 0;for (int i = 0; i <= k; i++){string temp="";for (int j = 0; j < i; j++){if (s[i]>s[j])  //要严格满足s[i]>s[j]才行{if (dp[j] + 1 > dp[i]){dp[i] = dp[j] + 1;path[i] = j;temp = s[j];  //将最新的s[j]保存}elseif (dp[j] + 1 == dp[i])  //如果等于,取最小的s[j]{if (temp>s[j])  //如果temp>s[j],说明temp比当前的s[j]大,我们要取小的{path[i] = j;temp = s[j];}}}}if (dp[i] > res){res = dp[i];start = i;}elseif (dp[i] == res)  // 可能有相同长度的子序列,需要挑选出字典序最小的{if(s[start]>s[i])  //如果之前的最小字典序的子序列大于等于当前的子序列,我们优先选择小的start = i;} }int i = 0;while (start != -1){a[i++] = start;start = path[start];}for (i = res - 1; i >= 0; i--)cout<

相关内容

热门资讯

常用商务英语口语   商务英语是以适应职场生活的语言要求为目的,内容涉及到商务活动的方方面面。下面是小编收集的常用商务...
六年级上册英语第一单元练习题   一、根据要求写单词。  1.dry(反义词)__________________  2.writ...
复活节英文怎么说 复活节英文怎么说?复活节的英语翻译是什么?复活节:Easter;"Easter,anniversar...
2008年北京奥运会主题曲 2008年北京奥运会(第29届夏季奥林匹克运动会),2008年8月8日到2008年8月24日在中华人...
英语道歉信 英语道歉信15篇  在日常生活中,道歉信的使用频率越来越高,通过道歉信,我们可以更好地解释事情发生的...
六年级英语专题训练(连词成句... 六年级英语专题训练(连词成句30题)  1. have,playhouse,many,I,toy,i...
上班迟到情况说明英语   每个人都或多或少的迟到过那么几次,因为各种原因,可能生病,可能因为交通堵车,可能是因为天气冷,有...
小学英语教学论文 小学英语教学论文范文  引导语:英语教育一直都是每个家长所器重的,那么有关小学英语教学论文要怎么写呢...
英语口语学习必看的方法技巧 英语口语学习必看的方法技巧如何才能说流利的英语? 说外语时,我们主要应做到四件事:理解、回答、提问、...
四级英语作文选:Birth ... 四级英语作文范文选:Birth controlSince the Chinese Governmen...
金融专业英语面试自我介绍 金融专业英语面试自我介绍3篇  金融专业的学生面试时,面试官要求用英语做自我介绍该怎么说。下面是小编...
我的李老师走了四年级英语日记... 我的李老师走了四年级英语日记带翻译  我上了五个学期的小学却换了六任老师,李老师是带我们班最长的语文...
小学三年级英语日记带翻译捡玉... 小学三年级英语日记带翻译捡玉米  今天,我和妈妈去外婆家,外婆家有刚剥的`玉米棒上带有玉米籽,好大的...
七年级英语优秀教学设计 七年级英语优秀教学设计  作为一位兢兢业业的人民教师,常常要写一份优秀的教学设计,教学设计是把教学原...
我的英语老师作文 我的英语老师作文(通用21篇)  在日常生活或是工作学习中,大家都有写作文的经历,对作文很是熟悉吧,...
英语老师教学经验总结 英语老师教学经验总结(通用19篇)  总结是指社会团体、企业单位和个人对某一阶段的学习、工作或其完成...
初一英语暑假作业答案 初一英语暑假作业答案  英语练习一(基础训练)第一题1.D2.H3.E4.F5.I6.A7.J8.C...
大学生的英语演讲稿 大学生的英语演讲稿范文(精选10篇)  使用正确的写作思路书写演讲稿会更加事半功倍。在现实社会中,越...
VOA美国之音英语学习网址 VOA美国之音英语学习推荐网址 美国之音网站已经成为语言学习最重要的资源站点,在互联网上还有若干网站...
商务英语期末试卷 Part I Term Translation (20%)Section A: Translate ...