当前位置: 首页 > news >正文

动态规划(数位统计dp 状态压缩dp 树形dp 记忆化搜索) from y总

一.数位统计dp 

题目描述

给定两个整数 a 和 b,求 a 和 b 之间的所有数字中 0∼9 的出现次数。例如,a=1024,b=1032,则 a 和 b 之间共有 9 个数如下:1024 1025 1026 1027 1028 1029 1030 1031 1032,其中‘0’出现 10 次,‘1’出现 10 次,‘2’出现 7 次,‘3’出现 3 次等等...

输入格式

输入包含多组测试数据。每组测试数据占一行,包含两个整数 a 和 b。当读入一行为 0 时,表示输入终止,且该行不作处理。

输出格式

每组数据输出一个结果,每个结果占一行。每个结果包含十个用空格隔开的数字,第一个数字表示‘0’出现的次数,第二个数字表示‘1’出现的次数,以此类推。

数据范围

0<a,b<100000000

输入样例

1 10
44 497
346 542
1199 1748
1496 1403
1004 503
1714 190
1317 854
1976 494
1001 1960
0 0

输出样例

1 2 1 1 1 1 1 1 1 1
85 185 185 185 190 96 96 96 96 95 93
40 90 40 91 136 82 30 40 40 40 40
118 866 215 215 214 206 205 154 105 106
18 119 19 30 114 20 20 19 19 16
417 185 100 101 191 197 200 200 700 200
103 1133 505 503 503 502 502 417 402 412
196 513 106 104 97 93 97 97 142 106
398 1375 398 398 405 409 409 495 488 471
294 1336 296 296 296 298 297 296 296 247

思路:此类问题最重要的是分类讨论

如果挨个算,一定会超时。对于每一对数a和b,我们可以算出1~a中0~9出现的次数p,再算出1~b中0~9出现的次数q,用类似于前缀和的思想,那么答案就是q-p

对于0~9出现的次数,我们可以算出每个数字在每一位上出现的次数。比如我们要算1出现的次数,对于数abcdefg,可以分别求出1在每一位上出现的次数

代码实现

 #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int P = 1e9+7;
const int N = 1e3+10;int get(vector<int> num,int l,int r)//求前面的数字方案
{int res= 0 ;for(int i = l;i>=r;i--){res = res*10 + num[i];}return res;
}int power(int x)//求出后面有几位数
{int res = 1;while(x--){res *=10;}return res;
}int count(int n,int x)
{if(n==0) return 0;vector<int> num;//把n的每一位求出来while(n){num.push_back(n/10);n/=10;}n = num.size();int res = 0;for(int i = n-1-!x;i>=0;i--){if(i<n-1){res += get(num,n-1,i+1) * power(i);//与上图中的abc*1000同理if(!x) res -= power(i);//如果求0的次数,是从001开始}if(num[i]==x) res += get(num,i-1,0)+1;//与上图中的(2,2)同理else if(num[i]>x) res += power(i);//与上图中的(2,3)同理}return res;
}int main() 
{int a,b;while(cin>>a>>b,a||b){if(a>b) swap(a,b);//保证a是较小数,b是较大数for(int i = 0;i<10;i++)cout<<count(b,i)-count(a-1,i)<<' ';//前缀和思想cout<<endl;}return 0;
}

二.状态压缩dp

题目一

求把 N×M 的棋盘分割成若干个 1×2 的长方形,有多少种方案。
例如当 N=2,M=4 时,共有 5 种方案。当 N=2,M=3 时,共有 3 种方案。

输入格式

输入包含多组测试用例。
每组测试用例占一行,包含两个整数 N 和 M。
当输入用例 N=0,M=0 时,表示输入终止,且该用例无需处理。

输出格式

每个测试用例输出一个结果,每个结果占一行。

数据范围

1≤N,M≤11

输入样例

1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0

输出样例

1  
0  
1  
2  
3  
5  
144  
51205  

思路:如果我们将所有横着放的长方形方案都求出来,那么这样的方案也包含了所有竖着放的方案,所以只考虑怎么横着放就可以。

对于合法的方案,有两个条件:

1.如果在第i行的第j列放置,且在第k行的第j列放置(k>i),那么k-i必须是偶数,否则竖着放会重叠

2.如果在第i行的第j列放置,那么第i-1行的第j列不能有长方形

dp[i][j]表示摆到了第i列,上一列的摆放状态是j的情况下,总共的方案数。比如当n=5,时,摆到了第2列,在上一列中,第1行和第四行摆放了长方形,那么若用1表示有长方形,0表示没有长方形,那么j的二进制就是10010.

对于条件一,可以先预处理一下,用数组标记true or false。

代码实现

 #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int P = 1e9+7;
const int N = 12,M = 1 << N;ll dp[N][M];
bool st[M];int main() 
{int n,m;while(cin>>n>>m,n||m){memset(dp,0,sizeof dp);//对条件一预处理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)//如果第j位是1{if(cnt & 1) st[i] = false;//如果0的数量是奇数cnt = 0;//0的数量清零,从下一个0开始重新计数}else cnt++;//如果第j位是0,那么0的数量+1}if(cnt & 1) st[i] = false;}dp[0][0] = 1;//初始化for(int i = 1;i<=m;i++){for(int j = 0;j<1<<n;j++)//j表示第i列的方案{for(int k = 0;k<1<<n;k++)//k表示第i-1列的方案{//如果长方形上下两行不重叠且0的数量是奇数,说明合法if((j & k) ==0 && st[j | k]){dp[i][j] += dp[i-1][k];}}}}cout<<dp[m][0]<<endl;//因为是从第0列开始,那么只有当m-1列没有横着放置时才合法}return 0;
}

题目二

给定一张 n 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短 Hamilton 路径。Hamilton 路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。

输入格式

第一行输入整数 n
接下来 n 行每行 n 个整数,其中第 i 行第 j 个整数表示点 i 到 j 的距离(记为 a[i][j])。

对于任意的 x, y, z,数据保证 a[x][x]=0a[x][y]=a[y][x] 并且 a[x][y]+a[y][z]≥a[x][z]

输出格式

输出一个整数,表示最短 Hamilton 路径的长度。

数据范围

1 ≤ n ≤ 20
0 ≤ a[i][j] ≤ 10^7

输入样例

5
0 2 4 5 1
2 0 6 5 3
4 6 0 8 3
5 5 8 0 5
1 3 3 5 0

输出样例

18

思路:

dp[state][u]:表示 “已访问的点集为 state,且当前位于点 u 时的最短路径长度”。

state:用二进制数表示 “已访问的点集”(M = 1 << N,即 2^20 种可能)

u:当前所在的点(0 ≤ u < n)。


若当前状态 i 包含点 j,则 j 一定是从某个已访问的点 k 转移而来(k 在 j 之前被访问)。
因此,从 “未访问 j 时的状态(i - (1 << j))、位于点 k” 的最短路径,加上 k 到 j 的距离 w[k][j],即可更新当前状态的最短路径。

代码实现

 #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int P = 1e9+7;
const int N = 20,M = 1 << N;ll dp[M][N];
ll w[N][N];int main() 
{int n;cin>>n;for(int i = 0;i<n;i++){for(int j = 0;j<n;j++){cin>>w[i][j];}}memset(dp,0x3f,sizeof dp);dp[1][0] = 0;for (int i = 0; i < 1 << N; i++){  // 遍历所有可能的点集状态(state = i)for (int j = 0; j < n; j++) {   // 遍历当前可能位于的点jif (i >> j & 1){  // 若状态i中包含点j(即j已被访问)// 寻找上一个位于的点k(k在j之前被访问)for (int k = 0; k < n; k++) {  // 检查:去掉j后的状态(i - (1<<j))中是否包含k(即k已被访问)if ((i - (1 << j)) >> k & 1) {  // 从k转移到j:更新dp[i][j]为更短的路径dp[i][j] = min(dp[i][j], dp[i - (1 << j)][k] + w[k][j]);}}}}}cout<<dp[(1<<n)-1][n-1];return 0;
}

三.树形dp

Ural 大学有 N 名职员,编号为 1∼N,他们的关系构成一棵以校长为根的树,父节点是子节点的直接上司。每个职员有一个快乐指数 Hi​(1≤i≤N)。

现在要召开一场周年庆宴会,条件是没有职员愿意和直接上司一起参会。在满足该条件的前提下,希望邀请一部分职员参会,使得所有参会职员的快乐指数总和最大,求这个最大值。

输入格式

  1. 第一行:一个整数 N,表示职员数量。

  2. 接下来 N 行:第 i 行表示 i 号职员的快乐指数 Hi​。

  3. 接下来 N−1 行:每行输入一对整数 L,K,表示 K 是 L 的直接上司。

  4. 最后一行:输入 0 0,作为输入结束标志(实际处理时可用于判断输入终止 )。

输出格式

输出一个整数,表示最大的快乐指数总和。

数据范围

1≤N≤6000

−128≤Hi​≤127

输入样例

7  
1  
1  
1  
1  
1  
1  
1  
1 3  
2 3  
6 4  
7 4  
4 5  
3 5  
0 0  

输出样例

5

思路:

dp[u][0]表示所有从以u为根的子树中选择,并且不选u这个点的方案

dp[u][1]表示所有从以u为根的子树中选择,并且选u这个点的方案

dp[u][0] = ∑max( dp[si][0] , dp[si][1] )

dp[u][1] = ∑dp[si][0]

si表示u的每个儿子

代码实现

 #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int P = 1e9+7;
const int N = 1e4+10;int n;
int happy[N];
int h[N],e[N],ne[N],idx;//用邻接表存树
bool has[N];//判断一个点有没有父节点
int dp[N][2];void add(int a,int b)
{e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}void dfs(int u)
{dp[u][1] = happy[u];for(int i = h[u];i!=-1;i = ne[i]){int j = e[i];dfs(j);dp[u][0] += max(dp[j][0],dp[j][1]);dp[u][1] += dp[j][0];}
}int main() 
{cin>>n;for(int i = 1;i<=n;i++) cin>>happy[i];memset(h,-1,sizeof h);for(int i = 0;i<n;i++){int a,b;cin>>a>>b;has[a] = true;//a有父节点add(b,a);}int root = 1;while(has[root]) root++;//找到树的根节点dfs(root);cout<<max(dp[root][0],dp[root][1]);return 0;
}

四.记忆化搜索

给定一个 R 行 C 列的矩阵,表示一个矩形网格滑雪场。矩阵中第 i 行第 j 列的点表示滑雪场的第 i 行第 j 列区域的高度。

一个人从滑雪场中的某个区域内出发,每次可以向上下左右任意一个方向滑动一个单位距离,当然,一个人能够滑动到某相邻区域的前提是该区域的高度低于自己目前所在区域的高度。

下面给出一个矩阵作为例子:
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

在给定矩阵中,一条可行的滑行轨迹为 24 - 17 - 2 - 1。
在给定矩阵中,最长的滑行轨迹为 25 - 24 - 23 - … - 3 - 2 - 1,沿途共经过 25 个区域。

现在给定你一个二维矩阵表示滑雪场各区域的高度,请你找出在该滑雪场中能够完成的最长滑雪轨迹,并输出其长度(可经过最大区域数)。

输入格式
第一行包含两个整数 R 和 C。
接下来 R 行,每行包含 C 个整数,表示完整的二维矩阵。

输出格式
输出一个整数,表示可完成的最长滑雪长度。

数据范围
1 ≤ R,C ≤ 300,
0 ≤ 矩阵中整数 ≤ 10000

输入样例

5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

输出样例

25

思路:

dp[i,j]表示 所有从(i,j)开始滑的路径长度的最大值

状态转移: dp[i][j] = max( dp[i][j+1] , dp[i+1][j] , dp[i][j-1] , dp[i-1][j] ) + 1

代码实现

 #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int P = 1e9+7;
const int N = 1e3+10;int n,m;
int dp[N][N];
int w[N][N];
int dx[4] = {-1,0,1,0},dy[4] = {0,1,0,-1};int solve(int x,int y)
{int &v = dp[x][y];//令v代替dp[x][y],后面写v等价于写dp[x][y]if(v!=-1) return v;//如果已经这个点已经搜索过,直接返回v = 1;for(int i = 0;i<4;i++){int xx = x+dx[i],yy = y+dy[i];if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&w[xx][yy]<w[x][y])//如果在边界内并且符合从高到低{v = max(v,solve(xx,yy)+1);}   }return v;
}int main() 
{;cin>>n>>m;for(int i = 1;i<=n;i++){for(int j = 1;j<=m;j++) cin>>w[i][j];}memset(dp,-1,sizeof dp);//初始化int res = 0;for(int i = 1;i<=n;i++){for(int j = 1;j<=m;j++){res=  max(res,solve(i,j));}}cout<<res<<endl;return 0;
}

http://www.dtcms.com/a/309162.html

相关文章:

  • 【C语言】字符函数与字符串函数详解
  • http请求访问响应慢问题解决的基本思路
  • 基于python大数据的招聘数据可视化及推荐系统
  • natapp的报错Tunnel StatusReconnecting...
  • STM32芯片简述
  • 使用GPU和NPU视频生成的优劣对比
  • 人工智能与金融:金融服务的重塑
  • Linux9 root密码修改
  • armbian 启用nginx并设置访问密码
  • CTF实战:用Sqlmap破解表单输入型SQL注入题(输入账号密码/usernamepassword)
  • SpringBoot AI应用实战:从图像识别到预测分析
  • 【通用视觉框架】基于OpenCvSharp+WPF+YOLO开发的仿VisionMaster的通用视觉框架软件,全套源码,开箱即用
  • 机器人芯片:智能机器的“大脑”与未来趋势
  • Nature Machine Intelligence 面向机器人操作有效滑移控制的仿生轨迹模块
  • alaxea机器人由星海图人工智能科技有限公司研发的高性能仿人形机器人
  • 【LeetCode 热题 100】155. 最小栈
  • PL-0功能拓展及基于VSCode的IDE配置
  • kotlin语法和特性分析
  • PDFsam免费开源!PDF分割合并工具
  • 华为数通HCIP
  • 为什么我们需要提示词增强工程PEE(Prompt Enhancement Engineering )
  • axios请求的取消
  • ICML 2025 | 深度剖析时序 Transformer:为何有效,瓶颈何在?
  • Qt Quick 3D 基础与应用
  • 【数据结构初阶】--排序(一):直接插入排序,希尔排序
  • zabbix平台无法删除已停用主机的处理案例
  • 基于springboot的快递分拣管理系统
  • 信号发生器和示波器阻抗匹配问题
  • 重生之我在暑假学习微服务第七天《微服务之服务治理篇》
  • flutter设备图标颜色与字体颜色相同自动适配明与暗的模式