南华大学20级ACM队进阶动态规划练习解题报告

1.石子合并(区间DP)

题目链接:Acwing 石子合并

题目分析:最后合并的石子堆的最优解可以分解为求合并前两堆石子的最优解,母问题可以被分解为子问题解决,因此可以选择DP解决

//#pragma GCC optimize(2)
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<ctime>
#include<cstring>
#include<list>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef  pair<int, int> PII;
const int N = 1e3 + 7;int n;
int a[N];
int s[N];  //前缀和数组
int dp[N][N];  //dp[i][j]代表i~j区域之间的最优解void solve()
{cin >> n;for (int i = 1; i <= n; i++)cin >> a[i], s[i] = s[i - 1] + a[i];for (int le = 2; le <= n; le++)  //枚举区间长度,区间长度最小为2{for (int i = 1; i + le - 1 <= n; i++)  //枚举起点{dp[i][i + le - 1] = 1e8;for (int j = i; j <= i + le - 1; j++){   //枚举区间所有的合并方式,找出最优解dp[i][i + le - 1] = min(dp[i][i + le - 1], dp[i][j - 1] + dp[j][i + le - 1] + s[i + le - 1] - s[i - 1]);}}}//输出答案cout << dp[1][n] << endl;
}int main()
{//std::ios::sync_with_stdio(false);//cin.tie(0), cout.tie(0);solve();return 0;
}

2.环形石子合并(环形区间DP)

题目链接:LibreOJ 能量项链

题目分析:这题与上一题的区别就是区间由直线变成了环形,而解决环形问题的方法就是将原数组复制一份使数组长度变为两倍,然后处理方法与第一题相同。

//#pragma GCC optimize(2)
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<ctime>
#include<cstring>
#include<list>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef  pair<int, int> PII;
const int N = 1e3 + 7;int n;
int a[N];  //原数组
int s[N];  //前缀和
int dp[N][N];  //最小值
int dp2[N][N];  //最大值void solve()
{cin >> n;for (int i = 1; i <= n; i++)cin >> a[i], s[i] = s[i - 1] + a[i];for (int i = 1; i <= n; i++)  //数组延长为两倍s[i + n] = s[n + i - 1] + a[i];int ma = -0x3f3f3f3f;int mi = 0x3f3f3f3f;mem(dp, 0x3f);  //初始化dp数组mem(dp2, -0x3f);for (int le = 1; le <= n; le++)  //枚举长度{for (int i = 1; i + le - 1 <= 2 * n; i++)   //枚举起点,记得枚举到2*n{if (i == i + le - 1)  dp[i][i + le - 1] = dp2[i][i + le - 1] = 0;elsefor (int j = i; j < i + le - 1; j++){   //枚举所有可能合并情况dp[i][i + le - 1] = min(dp[i][i + le - 1], dp[i][j] + dp[j + 1][i + le - 1] + s[i + le - 1] - s[i - 1]);dp2[i][i + le - 1] = max(dp2[i][i + le - 1], dp2[i][j] + dp2[j + 1][i + le - 1] + s[i + le - 1] - s[i - 1]);}}}for (int i = 1; i + n - 1 <= 2 * n; i++)  //找出最大值{ma = max(ma, dp2[i][i + n - 1]);mi = min(mi, dp[i][i + n - 1]);}cout << mi << endl;cout << ma << endl;
}int main()
{//std::ios::sync_with_stdio(false);//cin.tie(0), cout.tie(0);solve();return 0;
}

3.能量项链(环形区间DP)

题目链接:LibreOJ 能量项链

题目分析:环形区间的处理方法与上一题类似,此题虽然说每一个项链有两个值,但是由于前一个柱子的后值必定等于后一个珠子,因此我们依然可以直接dp处理,只是枚举的长度必须从3开始。

//#pragma GCC optimize(2)
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<ctime>
#include<cstring>
#include<list>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef  pair<int, int> PII;
const int N = 210;int n;
int dp[N][N],w[N];void solve()
{cin >> n;for (int i = 1; i <= n; i++){cin >> w[i];w[i + n]=w[i];}mem(dp, 0);for (int len = 3; len <= n + 1; len++){for (int l = 1; l - 1 + len <= n * 2; l++){int r = l - 1 + len;for (int k = l + 1; k < r; k++)dp[l][r] = max(dp[l][r], dp[l][k] + dp[k][r] + w[l] * w[k] * w[r]);  //两个区间合并后要加上三个数字之积}}int res = 0;for (int i = 1; i <=2* n; i++)  //找出最大值输出res = max(res, dp[i][i + n]);cout << res << endl;
}int main()
{std::ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);solve();return 0;
}

4.二叉苹果树(树形DP)

题目链接:LibreOJ 二叉苹果树

题目分析:背包问题的变种,选取某件物品与选择另外的物品是有相关性的,其中还涉及到了邻接表的存图与遍历问题,第一次写还是有一定难度的。
邻接表知识点:链式前向星代码分析

//#pragma GCC optimize(2)
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<ctime>
#include<cstring>
#include<list>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef  pair<int, int> PII;
const int N = 110,M=N*2;int n, m;
int h[N], e[M], w[M], ne[M], id;  //邻接表所需数组与变量
int dp[N][N];void add(int a, int b, int c)  //邻接表的增加,在这里就不解释了
{w[id] = c;e[id] = b;ne[id] = h[a];h[a] = id;id++;
}void dfs(int u, int fa)
{for (int i = h[u];~ i; i = ne[i])//i遍历了所有的u的子节点{if (e[i] == fa)continue;//这是想认爹做子的逆子,由于存图时当作无向图处理,所以要把重边跳过dfs(e[i], u);for (int j = m; j >= 0; j--)//体积,选几条边{for (int k = 0; k < j; k++){   //遍历子节点判断是否在最后的答案中加上以i为根节点的子树,背包模型dp[u][j] = max(dp[u][j], dp[u][j - k - 1] + dp[e[i]][k] + w[i]);}}}
}void solve()
{cin >> n >> m;mem(h, -1);  //初始化h数组,邻接表知识点for (int i = 0; i < n - 1; i++){int a, b, c;cin >> a >> b >> c;//无向图存图方法,与上面判断是否为逆子对应add(a, b, c);add(b, a, c);}dfs(1, -1);  //1为父节点,h[1]=-1cout << dp[1][m];
}int main()
{//关闭同步流std::ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);solve();return 0;
}

5.没有上司的舞会(树形DP)

题目链接:LibreOJ 周年纪念晚会

题目分析:代码模板与上一题类似,但是状态转移方程需要做出改变,在代码中,我们要实现如果选择了某个领导,那他的直系下司就不会来参加舞会;如果上司不来,我们要判断他的直系下司到底来不来对最后的结果最有利。我们在dp数组中加一个深度为2的维度通过0,1来判断领导来不来。

//#pragma GCC optimize(2)
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<ctime>
#include<cstring>
#include<list>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef  pair<int, int> PII;
const int N = 6500;int n;
int h[N],  e[N], ne[N],id=1;  //邻接表
int hasBoss[N];  //用来寻找谁是没有上司的总经理
int happy[N];  //每个人的快乐程度
int dp[N][2];  //dp[i][0],dp[i][1]分别代表i号员工不来和来的最大快乐值
int boss;  //总经理的号码,根节点
void add(int a, int b)
{e[id] = b;ne[id] = h[a];h[a] = id++;hasBoss[b] = 1;
}void dfs(int u)  //u为员工编号
{dp[u][1] = happy[u];  //u号员工来,就加上他的快乐值for (int i = h[u]; i != -1; i = ne[i])  //邻接表的遍历{dfs(e[i]);  //递归dp[u][1] += dp[e[i]][0];  //u来了,直系下属就都不来dp[u][0] += max(dp[e[i]][0], dp[e[i]][1]);  //u不来,判断一下直系员工来或不来更加有利}
}void solve()
{cin >> n;for (int i = 1; i <= n; i++)cin >> happy[i];mem(h, -1);for (int i = 1; i <= n-1; i++){int x, y;cin >> x >> y;add(y, x);}for (int i = 1; i <= n; i++)  //寻找bossif (!hasBoss[i])boss = i;dfs(boss);cout << max(dp[boss][0], dp[boss][1]);}int main()
{//std::ios::sync_with_stdio(false);//cin.tie(0), cout.tie(0);solve();return 0;
}

6.国王(状压DP)

题目链接:LibreOJ 国王

题目分析:所谓状压,就是把图中每一行放不放棋子的状态用01二进制数存下来化为dp数组的一个维度,这样就用一个数字存下了一行的状态。大大节省了时间与空间。我们还可以用二进制运算来实现题目中的对放棋子的限制。然后,如果我们能确定第1~i行的情况,第i+1行的情况只与第i行的摆放方法有关,所以我们的dp方程的推导是由前一层得来。所以,我们除了要列出1~n层的情况外,还要列出第0行和n+1行。因为第一行的情况要由第0行推出。而题目最后的答案,就存在n+1行什么也不放的情况中。

如在代码中:
1.一行不能连着放两个国王----(state >> i & 1) && (state >> i + 1 & 1)!=1;
2.周围8个点不能放国王对下一行的限制----(a & b) && check(a | b)!=1;

提示:dp数组要开 long long,下方代码是使用了“ #define int long long ”才看不到long long

//#pragma gcc optimize(2)
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<ctime>
#include<cstring>
#include<list>
#define int long long
#define ull unsigned long long
#define inf 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef  pair<int, int> pii;
const int N = 12,M=1<<10,K=110;int n, m;
vector<int> state;
vector<int> head[M];
int cnt[M];
int dp[N][K][M];  //dp[i][j][k]---->前i行总共放了j个国王,第i行摆放情况为k的方案数bool check(int state)  //检查同一行有没有连续放两个国王的函数
{for (int i = 0; i < n; i++)if ((state >> i & 1) && (state >> i + 1 & 1))return false;return true;
}int count(int state)  //返回一行中国王个数的函数
{int res = 0;for (int i = 0; i < n; i++)res += state >> i & 1;return res;
}void solve()
{cin >> n >> m;for (int i = 0; i < 1 << n; i++)  //把所有符合条件1的二进制数存下来{if (check(i)){state.push_back(i);cnt[i] = count(i);}}for(int i=0;i<state.size();i++)  //然后再存下对于每一个符合条件1的数的所符合条件2的数for (int j = 0; j < state.size(); j++){int a = state[i], b = state[j];if (!(a & b) && check(a | b))head[i].push_back(j);}dp[0][0][0] = 1;  //第0层只能有什么都不放这一个选项的一种可能性for(int i=1;i<=n+1;i++)  //第i排for(int j=0;j<=m;j++)   //总共摆j个,相当于背包容积for(int a=0;a<state.size();a++)  //枚举第i行所有行摆放合法情况for (int b=0;b<head[a].size();b++)  //b代表i-1行对应a的合法摆放情况{int c = cnt[state[a]];  //c代表合法情况a的摆放的棋子的数量if (j >= c)  //如果背包容积大于物品dp[i][j][a] += dp[i - 1][j - c][b];   //状态转移求方案数}cout << dp[n + 1][m][0] << endl;   //输出结果,题目最后的答案就存在n+1行什么也不放的情况中。}signed main()
{//std::ios::sync_with_stdio(false);//cin.tie(0), cout.tie(0);solve();return 0;
}

练习时间:2021.7.20
作者:Avalon·Demerzel

【解题报告】动态规划进阶题(区间DP、树形DP、状压DP入门)相关推荐

  1. 刷题周记(九)——#状压DP:最短Hamilton路径、小国王(互不侵犯)、玉米田(Corn Fields G)、愤怒的小鸟、吃奶酪、炮兵阵地、宝藏 #区间DP:清空字符串#DP:关灯问题II

    文章目录 --2020年12月20日(周日)------------------ 状压DP 一.最短Hamilton路径(模板题) 二.玉米田(P1879 [USACO06NOV]Corn Field ...

  2. [状压dp] 小国王(状压dp+下标映射技巧)

    文章目录 0. 前言 1. 状压dp+棋盘式(基于连通性) 0. 前言 相关: [状压dp] 蒙德里安的梦想(模板题+状压dp) 1. 状压dp+棋盘式(基于连通性) 1064. 小国王 思路: 状压 ...

  3. 【CodeForces - 1051D】Bicolorings (dp,类似状压dp)

    题干: You are given a grid, consisting of 22 rows and nn columns. Each cell of this grid should be col ...

  4. 【进阶指南】玉米田【状压DP】

    Date:2022.04.08 题意描述: 农夫约翰的土地由 M×N 个小方格组成,现在他要在土地里种植玉米. 非常遗憾,部分土地是不育的,无法种植. 而且,相邻的土地不能同时种植玉米,也就是说种植玉 ...

  5. 状压dp之二之三 炮兵阵地/玉米田 By cellur925

    一.简单的状压dp 玉米田 题目描述 Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ ...

  6. ABC 215 E - Chain Contestant (状压DP)

    链接 题意: 给出一个只包括A~J的字符串,定义一种子序列为:在这个子序列中,相同的字符必定连续出现,求出这样的子序列有多少个. 数据范围:字符串长度1 <= n <= 1000 分析: ...

  7. 糖果(2019第十届蓝桥杯省赛C++A组I题) 解题报告(状压dp) Apare_xzc

    糖果(2019第十届蓝桥杯省赛C++A组I题) 解题报告(状压dp) xzc 2019/4/5 试题 I: 糖果 时间限制: 1.0s 内存限制: 256.0MB 本题总分:25分 [问题描述]    ...

  8. 牛客题单_动态规划课程状压dp习题

    牛客题单_动态规划课程状压dp习题 文章目录 牛客题单_动态规划课程状压dp习题 NC14732 锁 NC15034 德玛西亚万岁 NC16418 宝藏 NC17061 多彩的树 NC17890 方格 ...

  9. 动态规划 —— 状压 DP

    [概述] 通常将以一个集合内的元素信息作为状态且状态总数为指数级别的动态规划称为状态压缩动态规划. 其是一类以集合信息为状态的特殊的动态规划问题,主要有传统集合动态规划与基于连通性状态压缩的动态规划两 ...

  10. 【每日DP】day2、P1879 [USACO06NOV]Corn Fields G玉米地(状压DP模板题)难度⭐⭐⭐★

    昨天的每日DP我还在写01背包,今天就到状压DP了,真刺激. P1879 [USACO06NOV]Corn Fields G 题目链接 输入 2 3 1 1 1 0 1 0 输出 9 一道简单的状压D ...

最新文章

  1. 自学python可以找到好的工作吗-通过自学python能找到工作吗
  2. 一般编译器错误_Java程序员最容易犯的10个错误
  3. 子数组的最大累加和问题
  4. UNWAVERING SPIRIT AND VALUES
  5. Qt 编译错误 LINK2001:无法解析的外部符号 public: virtual struct QMetaObject const thiscall Widget::metaObject
  6. poj2976 Dropping tests
  7. 64 位系统 vs2013 配置 OpenCV-3.1.0
  8. 简支梁内力的计算机分析程序,各种静定梁内力的计算机模拟分析.pdf
  9. angular使用高德地图
  10. 社会性动物(艾略特•阿伦森)
  11. android 原生控件,抽离Android原生控件的方法
  12. pandas分组分析:GroupBy和pandas交叉分析:pivot_table/crosstab【学习记录】
  13. 禁止html5手机端双击页面放大的问题,主要针对苹果手机
  14. 虚幻4和Unity3D应该学哪个?
  15. 精密制造业行业_精密制造业的发展:精密制造业的深度报告
  16. 下载安装anaconda
  17. workon 未找到命令
  18. 认识服务器的几大必备知识
  19. 【CSDN】如何开启CSDN文章下的显示微信公众号、微信号、官方网站、QQ号、QQ群 ?
  20. 【转】可在广域网部署运行的QQ高仿版 -- GG叽叽(源码)

热门文章

  1. 4-MSP430定时器_定时器中断
  2. mysql搭建及数据迁移教程
  3. PCA MATLAB
  4. CSS3学习笔记——伪类hover
  5. cobbler装系统
  6. 关于跳转 + 传递消息,
  7. 补一天三层的东西,ACL
  8. HP刀片带外管理系统OA各功能实例示范
  9. LOJ 2743(洛谷 4365) 「九省联考 2018」秘密袭击——整体DP+插值思想
  10. 深入浅出分布式系统Raft协议