博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一些干净的DP题目
阅读量:3954 次
发布时间:2019-05-24

本文共 7666 字,大约阅读时间需要 25 分钟。

文章目录

EASY

给一个长为n(n<2000)的序列A,将其变成一个非增或非减的序列B的绝对值改变量。

在这里插入图片描述
key:B中所有的数都是A中的数。因为若 a l < b < a r a_l<b<a_r al<b<ar时,总能令b变成其中一个值。则枚举 b i b_i bi为每个A中的值即可。dp[i][j]表示 b i ≤ u n q j b_i≤unq_j biunqj时,目前的最小代价。

// #include
#include
#include
using namespace std;typedef long long ll;const int N = 2e3+4;int n,m;int a[N],unq[N];int dp[N][N];int solve(){
for(int i=0;i
>n; for(int i=0;i
>a[i]; unq[i]=a[i]; } sort(unq,unq+n); m=unique(unq,unq+n)-unq; int ans=solve(); reverse(a,a+n); ans=min(ans,solve()); cout<
<

题意:给出 m m m个时间区间,完成每个区间的事可以有一定的贡献。每做完一件事要缓 r r r分钟。问最多有多少贡献。

思路:由于时间范围为1e6。我和大部分题解双重循环区间不同。我遍历一遍时间。dp[i]表示前i分钟做的最大贡献。缓r分钟解决方式是每个区间的结束时间 + r +r +r。天坑:一个区间的ending_time可以开始另一件事了。

// #include
#include
#include
#include
using namespace std;typedef long long ll;const int N = 1e6+4,M=1e3+5;struct Node{
int l,v,nex;}e[M];int n,m,r;ll dp[N+N];unordered_map
head;void add(int a,int b,int c){
static int cnt=0; e[++cnt] = Node{
a,c,head.count(b) ? head[b] : 0}; head[b] = cnt;}int main(){
cin>>n>>m>>r; for(int a,b,c,i=1;i<=m;++i){
cin>>a>>b>>c; if(b<=n)add(a,b+r,c); } for(int i=1;i<=n+r;++i){
dp[i] = dp[i-1]; if(head.count(i))for(int j=head[i];j;j=e[j].nex){
dp[i]=max(dp[e[j].l]+e[j].v,dp[i]); } } cout<
<

给一个 n × n n×n n×n(n<1000)字符矩阵。求沿对角线丿对称的最大子矩阵。dp[siz][i][j]表示右下角坐标为(i,j),大小为siz+1的矩阵是否是对称矩阵。

// #include
#include
#include
#include
using namespace std;#define debug(_x) cout<<#_x<<": "<<_x<
>n&&n){
for(int i=0;i
>a[i]; reverse(a[i],a[i]+n); } for(int i=0;i

题意:给 n ( n ≤ 5000 ) n(n\le 5000) n(n5000)个数,选择一个起始点,可以让颜色相同的整段变色。问最少的变色次数,让所有颜色相同。

思路:假设选好了起始点 i i i,则如何求变色次数?显然答案是 n − 1 − f [ i − 1 ] [ i + 1 ] n-1-f[i-1][i+1] n1f[i1][i+1],其中 f [ i − 1 ] [ i + 1 ] f[i-1][i+1] f[i1][i+1]代表 c 1 ⋯ c i − 1 c_1\cdots c_{i-1} c1ci1 c n ⋯ c i + 1 c_n\cdots c_{i+1} cnci+1的最长公共子序列。则直接求原数组和逆序数组的最长公共子序列即可。 O ( n 2 ) O(n^2) O(n2)

#include
#define IOS ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);using namespace std;const int N = 5005;int f[N][N], c[N];int main(){
IOS; int n; cin >> n; for(int i = 1; i <= n; ++i){
cin >> c[i]; } n = unique(c + 1, c + n + 1) - c - 1; for(int i = 1; i <= n; ++i){
for(int j = n; j > 0; --j){
f[i][j] = max({
f[i-1][j], f[i][j+1], f[i-1][j+1] + (c[i] == c[j])}); } } int ans = 0; for(int i = 1; i <= n; ++i) ans = max(ans, f[i-1][i+1]); cout << n - 1 - ans << endl;}

MID

题意:有 n ( n ≤ 100 ) n(n\le 100) n(n100)个玻璃杯,每个玻璃杯的容量是 a i a_i ai,已有水量是 b i b_i bi,倒水有一半的损失。挑 k k k个杯子装水,求最大的装水量。

第一个思路:dp[i][k][j]表示 1... i 1...i 1...i个杯子,挑 k k k个,剩余容量为j的最大装水量。但是这个状态转移具有后效性,可能前面后面要的杯子,用前面的杯子里的水。
题解dp[i][k][j]表示 1... i 1...i 1...i个杯子,挑 k k k个,总容量为 j j j的最大水量(未考虑是否装的下,只考虑有多少水)。状态转移如下。则取 k k k杯的答案就是max(j, dp[i][k][j])。时间复杂度 O ( n 3 ⋅ max ⁡ a ) O(n^3\cdot \max a) O(n3maxa),达到1e8。

dp[i][k][j]=max(dp[i-1][k][j] + b[i]/2, dp[i-1][k-1][j-a[i]] + b[i]);
#include
#define IOS ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);using namespace std;using ll = long long;const int N = 1e2 + 5;int dp[N][N][10005];int a[N], b[N], ans[N];int main(){
IOS; int n, CAP = 0; cin >> n; for(int i = 1; i <= n; ++i) cin >> a[i] >> b[i]; memset(dp, 0xf0, sizeof(dp)); dp[0][0][0] = 0; for(int i = 1; i <= n; ++i){
CAP += a[i]; for(int k = 0; k <= n; ++k){
for(int j = 0; j <= CAP; ++j){
int &now = dp[i][k][j]; now = dp[i-1][k][j] + b[i]; if(k && j - a[i] >= 0)now = max(now, dp[i-1][k-1][j-a[i]] + 2 * b[i]); if(ans[k] < min(now, 2 * j))ans[k] = min(now, 2 * j); } } } for(int k = 1; k <= n; ++k) cout << fixed << setprecision(6) << ans[k] / 2.0 << " "; cout << endl;}

题意:给定 n × m n×m n×m矩阵,每行选最多 m / 2 m/2 m/2个。求能被 m o d mod mod整除的最大和。(所有数小于70)

思路:对于第 i i i行来说, f [ j ] [ h ] [ k ] f[j][h][k] f[j][h][k]表示 1... j 1...j 1...j h h h个,对 m o d mod mod取余为 k k k的最大和。易得转移公式
f [ j ] [ h ] [ k ] = max ⁡ ( f [ j − 1 ] [ h ] [ k ] ,          f [ j − 1 ] [ h − 1 ] [ ( m o d + k − a [ i ] [ j ] % m o d ) % m o d ] + a [ i ] [ j ] ) f[j][h][k]=\max(f[j-1][h][k],\\ \ \ \ \ \ \ \ \ f[j-1][h-1][(mod+k-a[i][j]\%mod)\%mod]+a[i][j]) f[j][h][k]=max(f[j1][h][k],        f[j1][h1][(mod+ka[i][j]%mod)%mod]+a[i][j])
最后, s u m [ i ] [ k ] = max ⁡ j , h f [ j ] [ h ] [ k ] sum[i][k]=\max_{j,h}{f[j][h][k]} sum[i][k]=maxj,hf[j][h][k],再对行进行dp
d p [ i ] [ k ] = max ⁡ 0 ≤ l a < m o d ( , d p [ i − 1 ] [ l a ] + s u m [ i ] [ ( m o d + k − l a ) dp[i][k] = \max_{0\le la<mod}(, dp[i-1][la] + sum[i][(mod+k-la)%mod]) dp[i][k]=0la<modmax(,dp[i1][la]+sum[i][(mod+kla)
最终答案就是 d p [ n ] [ 0 ] dp[n][0] dp[n][0],时间复杂度为 O ( n ⋅ m 2 ⋅ m o d ) O(n\cdot m^2 \cdot mod) O(nm2mod)

#include
#define IOS ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);using namespace std;using ll = long long;const int N = 73;int f[N][N/2][N], sum[N][N], dp[N][N];int main(){
IOS; int n, m, mod; cin >> n >> m >> mod; memset(dp, 0xf0, sizeof(dp)); memset(sum, 0xf0, sizeof(sum)); dp[0][0] = 0; for(int now, i = 1; i <= n; ++i){
memset(f[0], 0xf0, sizeof(f[0])); f[0][0][0] = 0; sum[i][0] = 0; for(int j = 1; j <= m; ++j){
cin >> now; for(int h = 0; h <= m / 2; ++h){
for(int k = 0; k < mod; ++k){
f[j][h][k] = f[j-1][h][k]; if(h && f[j][h][k] < f[j-1][h-1][(mod+k-now%mod) % mod] + now) f[j][h][k] = f[j-1][h-1][(mod+k-now%mod) % mod] + now; if(sum[i][k] < f[j][h][k]) sum[i][k] = f[j][h][k]; } } } for(int k = 0; k < mod; ++k){
for(int la = 0; la < mod; ++la){
dp[i][k] = max(dp[i][k], dp[i-1][la] + sum[i][(mod+k-la)%mod]); } } } cout << dp[n][0] << endl;}

题意:给 n ( n ≤ 500 ) n(n\le 500) n(n500)个数,若相邻两个数相同( a i = = a i + 1 a_i==a_{i+1} ai==ai+1)则可以用 a i + 1 a_i+1 ai+1代替。问最后合并出最少的项数。

思路

  • 先考虑只有相邻的多个数合并成一个数的所有情况。当可以合并成一个数时,这个数必然是唯一的。即 n u m [ i ] [ j ] num[i][j] num[i][j]表示把 a i ⋯ a j a_i\cdots a_j aiaj合并成一个数,那个数的值,如果不能的话, n u m [ i ] [ j ] = 0 num[i][j]=0 num[i][j]=0.就是一个简单的区间DP
  • 再考虑最少用多少个数即 n u m num num区间来得到整个 1 ⋯ n 1\cdots n 1n区间。设 f [ i ] f[i] f[i]表示 1 ⋯ i 1\cdots i 1i区间最少有多少个数。
    f [ i ] = min ⁡ 0 ≤ j < i f [ j ] + 1      ( i f   n u m [ j + 1 ] [ i ] ≠ 0 ) f[i] = \min_{0\le j<i}{f[j] + 1}\ \ \ \ (if\ num[j+1][i]\not=0) f[i]=0j<iminf[j]+1    (if num[j+1][i]=0)
#include
#define IOS ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);using namespace std;using ll = long long;const int N = 505;int num[N][N], f[N];int main(){
IOS; int n; cin >> n; for(int i = 1; i <= n; ++i) cin >> num[i][i]; for(int len = 2; len <= n; ++len){
for(int i = 1, j = i + len - 1; j <= n; ++i, ++j){
for(int k = i; k < j; ++k){
if(num[i][k] == num[k+1][j] && num[i][k]) num[i][j] = num[i][k] + 1; } } } f[0] = 0; for(int i = 1; i <= n; ++i){
f[i] = i; for(int j = 0; j < i; ++j){
if(num[j+1][i])f[i] = min(f[i], f[j] + 1); } } cout << f[n] << endl;}

转载地址:http://egkzi.baihongyu.com/

你可能感兴趣的文章
重磅 | 数据挖掘之父韩家炜:文本语料库的数据挖掘(附视频+PPT下载)
查看>>
干货汇总 | 你可能不知道的 Python Web 部署方式总结
查看>>
技术人再不懂区块链,你就OUT了?漫画版
查看>>
快收藏!史上最全的 Linux Shell 文本处理工具集锦
查看>>
一小时爬千万数据的新浪微博爬虫
查看>>
简约而不简单的 Django 新手图文教程
查看>>
重磅!阿里首次全面公开展示AI布局(附布局图/成绩单/六产业详解)
查看>>
谷歌大脑2017技术研究总结 | Jeff Dean执笔(附论文 & 数据集)
查看>>
最新中国一二三四五线城市排名出炉!去这些城市买房准没错!
查看>>
BAT人工智能生态时局图:全面战争爆发前夜
查看>>
Python交互式数据分析报告框架~Dash介绍
查看>>
Chrome 浏览器 必知必会的小技巧
查看>>
Python奇技淫巧,看看你知道几个
查看>>
资源&教程 | Python数据分析,详细的学习路径
查看>>
白话AI:看懂深度学习真的那么难吗?初中数学,就用10分钟
查看>>
中国癌症大数据出来了!每年126万例癌症死亡本可避免
查看>>
超全的 Linux 机器的渗透测试命令备忘表,共16表128条命令
查看>>
代码传奇 | 明明可以靠颜值 却用代码把人类送上了月球的女人——Margaret Hamilton
查看>>
教你用Java来玩答题(百万英雄/冲刺大会等)
查看>>
用Python做跳一跳外挂太浪费了,这种技能让你目瞪口呆
查看>>