目录
走楼梯 (斐波那契)
(一次走1或2阶) f(n) = f(n - 1) + f(n - 2);
兔子生兔子 ---斐波那契
直线分割立体图形 , f(n) = f(n - 1) + n;输入:
5输出:
8
8
#include <iostream>
using namespace std;
const int N_01 = 1e3; //规范性写法
typedef long long ll; //简写
ll f_01[N_01];
void test_01() {
int n;
cin >> n;
f_01[0] = f_01[1] = 1;
for(int i = 2; i <= n; i++) { //写法一
f_01[i] = f_01[i - 1] + f_01[i - 2];
}
cout<< f_01[n] << endl;
ll a = 1, b = 1, c = 1;
for(int i = 2; i <= n; i++) { //写法二
c = a + b;
a = b;
b = c;
}
cout << c <<endl;
return;
}
杨辉三角 (dp - 二维数组)
杨辉三角递推式: (多项式高次展开 -- 系数)
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
(对角线、第一列为1)f[i][j] = f[i - 1][j - 1] + f[i - 1][j] //上面一层的元素加和 等于下面一层
const int N_triangle = 55;
long long f_triangle[N_triangle][N_triangle];
void init() {
for(int i = 1; i <=N_triangle; i++) { //这样是从 1 , 1开始
for(int j = 1; j <= i ; j++) {
if(i == 1) {
f_triangle[i][j] = 1;
} else {
f_triangle[i][j] = f_triangle[i - 1][j - 1] + f_triangle[i - 1][j];
}
}
}
return;
}
得到对应位上的值
输入:
3 3输出:
1
void test_init() {
init();
int n,m;
cin>> n >> m;
cout<< f_triangle[n][m]<<endl; //查看 坐标值
for(int i = 0; i <= n; i++) { //i <= n ,j最后为一次为 n
for(int j = 1; j <= i ; j++) { //i最后为执行的为 n - 1带入j停止
cout<< f_triangle[i][j] << " ";
}
cout<<endl;
}
}
void Yang_hui_triangle() { //my_AC
int n;
cin >> n;
for(int i = 0; i < n; i++) {
f[i][0] = 1;
f[i][i] = 1;
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
f[i][j] = f[i - 1][j - 1] + f[i - 1][j];
}
}
for(int i = 0; i < n; i++) {
for(int j = 0; j <= i; j++) { //i最后为执行的为 n - 1带入j停止
cout<< f[i][j] << " ";
}
cout<<endl;
}
return;
}
过河卒
分析: 如果直接搜索从A到B的所有路径,计算速度很慢用递推式求出起点到终点的路径数目
F(i,j) = F(i - 1 , j) + F(i , j - 1)边界条件 (0,0) , F(0,0) = 1; //控制点设为0
const int N_02 = 1000;
long long f_02[N_02][N_02];
void test_02() {
int n = 4,m = 8;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(i != 0) {
f_02[i][j] = f_02[i][j] + f_02[i - 1][j];
}
if(j != 0) {
f_02[i][j] = f_02[i][j] + f_02[i][j - 1];
}
}
}
return;
}
走马
dp法(动态规划 -- 递推式)
有多少种方案输入n,m,cx,cy
n,m 棋盘行列大小 ,cx,xy 马的坐标位置输入:
5 5 2 4输出:
14
int dir_03[8][2] = { {1,2},{2,1},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1} };
bool d_03[30][30]; //标记状态
long long dp_03[30][30]; //long long 递推式
void test_03() {
int n,m,cx,cy; //n,m 棋盘行列大小 ,cx,xy 马的坐标位置
cin >> n >> m >> cx >> cy ;
d_03[cx][cy] = true;//设为起始true可走
for(int i = 0; i < 8; i++) {
int tx = cx + dir_03[i][0];
int ty = cy + dir_03[i][1];
if(tx >= 0 && tx <= n && ty >= 0 && ty <=m ) {
d_03[tx][ty] = true;
}
}
dp_03[0][0] = 1;//起始值 ,临界值 !!
for(int i = 0; i <= n; i++) { //遍历棋盘 --动态规划--递推式
for(int j = 0; j <= m; j++) {
if(d_03[i][j] == false) {
if(i) { //i != 0 才能从上走
dp_03[i][j] += dp_03[i - 1][j];
}
if(j) { // j != 0 才能从左走
dp_03[i][j] += dp_03[i][j - 1];
}
}
}
}
cout << dp_03[n][m] <<endl; //有多少种方案
return;
}
动态规划算法常用于某种最优解 ,求出多种可行性 ,找出最优解状态
决策
状态转移方程
需最优化原理 :前一个策略状态必须最优
最优策略的子策略也是最优的
回家最短路径
输入: 地点两两之间距离(x,y坐标系) ,几个中转点
3
0 3 4
6 2 5
5 4 3输出:
12
#include<algorithm>
int a_04[110][110];
int dp_04[110][110];
void test_04() {
int n;
cin >> n;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
cin >> a_04[i][j];
}
}
dp_04[1][1] = 0;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
if(i == 1 && j == 1) {
continue;
} else if(i == 1) {
dp_04[i][j] = dp_04[i][j - 1] + a_04[i][j];
} else if(j == 1) {
dp_04[i][j] = dp_04[i - 1][j] + a_04[i][j];
} else {
dp_04[i][j] = min(dp_04[i - 1][j],dp_04[i][j - 1]) + a_04[i][j]; //最短
}
}
}
cout<< dp_04[n][n] <<endl;
return;
}
已知状态转移方程 ,求最大值
f[i][j] += max(f[i - 1][j],f[i - 1][j - 1]);
输入:
4
3
1 2
6 2 3
3 5 4 1
输出:
15
const int N_05 = 1e3 + 9;
const int inf_05 = 1000000000; //1e9
int f_05[N_05][N_05];
void test_05() { //bug
int n;
cin >>n; //行数
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= i; j++) {
cin >> f_05[i][j];
}
}
int ma = -inf_05;
for(int i = 1; i <= n; i++) {
for(int j = 1; i <= i; j++) {
f_05[i][j] += max(f_05[i - 1][j],f_05[i - 1][j - 1]);
if(i == n) {
ma = max(f_05[i][j],ma);
}
}
}
if(ma == -inf_05) {
ma = 0;
}
cout << ma <<endl;
//cout << -inf_05 <<endl;
return;
}
回家问题的递推式
f[i][j][k] = min ( min(f[i - 1][j][k] , f[i][j - 1][k]) ,f[i][j][k - 1] ) + f[i][j][k];
输入:
2 2 2
1 2 3
4 5 6
7 8 9
1 2 3
4 5 6
7 8 9
1 2 3
4 5 6
7 8 9输出:
23
const int N_06 = 1e2 + 9;
const int inf_06 = 1000000000; //1e9 //保证更新
int f_06[N_06][N_06][N_06];
void test_06() {
int x,y,z;
cin >> x >> y >> z;
for(int i = 0; i <= x; i++) {
for(int j = 0; j <= y ; j++) {
for(int k = 0; k <= z; k++) {
cin >> f_06[i][j][k];
}
}
}
for(int i = 0; i <= x; i++) {
for(int j = 0; j <= y ; j++) {
for(int k = 0; k <= z; k++) {
int mi = inf_06; //保证更新
if(i != 0) { //边界i == 0不能走 i - 1 , 即 i != 0的情况单独讨论,同理
mi = min(mi , f_06[i - 1][j][k]);
}
if(j != 0) {
mi = min(mi , f_06[i][j - 1][k]);
}
if(k != 0) {
mi = min(mi , f_06[i][j][k - 1]);
}
if(mi != inf_06) {
f_06[i][j][k] += mi;
}
}
}
}
cout<< f_06[x][y][z] <<endl;
return;
}
动态规划入门题
爬楼梯 (一次最多只能跨两个台阶 , 即1 或 2)
输入:
4输出:
5
const int mod_07 = 100007; //答案可能很大输出答案 对100007取模 的结果
int dp_07[1010];
void test_07() {
int n;
cin >> n;
dp_07[1] = dp_07[0] = 1; //边界
for(int i = 2; i <= n; i++) {
dp_07[i] = (dp_07[i - 1] + dp_07[i - 2] ) % mod_07 ; //状态转移方程
}
cout<< dp_07[n] <<endl;
return;
}
爬楼梯升级:每次可以跳跃任意奇数的台阶
问有多少种放法爬到楼顶
输入:
4输出:
3
const int mod_08 = 100007; //答案可能很大输出答案 对100007取模 的结果
int dp_08[1010];
void test_08() {
int n;
cin >> n;
dp_08[0] = 1; //边界
for(int i = 1; i <= n; i++) {
dp_08[i] = 0;//初始值
for(int j = i - 1; j >= 0; j -= 2) { //奇数阶
dp_08[i] += dp_08[j] ; //每个遍历 从上往下降级 加组合
dp_08[i] %= mod_08;
}
}
cout<< dp_08[n] <<endl;
return;
}
弹小球
输出一个整数,表示经过多少次才能弹出弹簧板
输入:
5
2 2 3 1 2输出:
3
#include<cstring>
const int maxn_09 = 100010;
int a_09[maxn_09] , dp_09[maxn_09];
void test_09() {
int n;
cin >> n;
memset(dp_09,0,sizeof(dp_09)); //清零 ,初始化 memset(数组名,赋值,数组字节大小)
for(int i = 1; i <= n; i++) {
cin >> a_09[i];
}
int ans = 0;
for(int i = n; i >= 1; i--) {
dp_09[i] = dp_09[i + a_09[i]] + 1;
ans = max(ans , dp_09[i]);
}
cout << ans << endl;
return;
}
传娃娃
n, m n个人玩游戏 ,传m次娃娃
问有多少种传娃娃的方法?
输入:
3 3输出:
2
int f_10[35][35];
void test_10() {
int n,m;
cin >> n >> m;
memset(f_10,0,sizeof(f_10)); //清零 ,初始化 memset(数组名,赋值,数组字节大小)
f_10[0][1] = 1;
for(int i = 1; i <= m; i++) {
for(int j = 1; j <= n; j++) {
if(j == 1) {
f_10[i][j] = f_10[i - 1][2] + f_10[i - 1][n]; //1号可以从上一次的传过来
} else if(j == n) {
f_10[i][j] = f_10[i - 1][1] + f_10[i - 1][n];
} else {
f_10[i][j] = f_10[i - 1][j - 1] + f_10[i - 1][j + 1];
}
}
}
cout<< f_10[m][1] << endl;
return;
}
逃生-生命值
#include<cstdio>
const int inf_11 = 0x3f3f3f3f;
const int maxn_11 = 1010;
int a_11[maxn_11][maxn_11];
int dp_11[maxn_11][maxn_11];
void test_11() {
int n,m,x,y,v,c; //n,m地图大小 , x,y初始位置 , v初始化血量 , c生命值上限
cin >> n >> m>> x >> y >> v >> c;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
scanf("%d",&a_11[i][j]);
}
}
int xx[4] = {-1,-1,1,1};
int yy[4] = {-1,1,-1,1};
for(int t = 0; t < 4; t++) {
for(int i = x; i > 0 &&i <= n; i++) {
for(int j = y; j > 0 && j <= m; j -= yy[t]) {
if(i == x && j == y) {
dp_11[i][j] = v;
} else if(i == x) {
dp_11[i][j] = min(c , dp_11[i][j + yy[t]] + a_11[i][j] );
} else if(i == x) {
dp_11[i][j] = min(c , dp_11[i + xx[t]][j] + a_11[i][j] );
} else {
dp_11[i][j] = min(c ,max( dp_11[i + xx[t]][j] ,dp_11[i][j + yy[t]] ) ) + a_11[i][j] ;
}
if(dp_11[i][j] < 0) {
dp_11[i][j] = -inf_11; //死了
}
}
}
}
int ans = max ( (dp_11[1][1],dp_11[1][m]) , max(dp_11[n][1],dp_11[n][m]) );
if(ans <= 0) {
cout << "-1" <<endl;
} else {
cout << ans <<endl;
}
return;
}
一维消消乐
n颗珠子排成一排 , 每一颗珠子的价值wi(可能是负数)
可以选择若干对相邻的珠子 ,两个珠子消一次,且消去后还会占位
输出最大分数
输入:
8
-9 -5 -4 -2 4 -5 -4 2输出:
73
const int maxn = 10010;
int dp[maxn][2];
int w[maxn];
void test_12() {
int n;
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> w[i];
}
dp[1][0] = 0;
for(int i = 2;i <= n;i++){
dp[i][0] = max( dp[i - 1][0] ,dp[i - 1][1] ); //合
dp[i][1] = dp[i - 1][0] + w[i] * w[i - 1] ; // 没合
}
cout<< max( dp[n][0] , dp[n][1]) <<endl;
return;
}
墙壁涂色
3种颜色 用(1/2/3表示)
相邻颜色不相同 , 输入n,表示房间被划分成多少部分 ,输出有几种分法
输入:
4输出:
18
typedef long long ll;
void test_13(){
int n;
ll a[51];
a[1] = 3;
a[2] = 6;
a[3] = 6;
for(int i = 4;i <= 50;i++){
a[i] = a[i - 1] + a[i - 2] * 2; //第n个 要考虑第n-1个是不是相同
}
cin >> n;
cout<< a[n] <<endl;
return;
}
int main() {
test_13();
return 0;
}