dp好难啊啊啊啊啊啊啊啊啊啊

HDU1024 Max Sum Plus Plus


题目大意就是给你一个序列从里面截出连续m段使每一段区间不相交并且和最大

思路就是:

集合表示 :我们先确定状态dp[i][j] 就是所有划分方法的和的集合

状态表示 :以第j个数结尾分为i组的最大和是多少

集合划分:对于第j个物体我们可以说它自己自成一组或者加入最后一组

1. dp[i][j - 1] + a[j],前j - 1个物体分为i组的最大值a[j]这个加在最后一组里面的最大值

2.dp[i - 1][k] + a[j] (k >= i - 1 && k < j) k个物体分成i - 1组的最大值然后再加上a[j]自己成一组

我们发现这个是n ^ 3 的复杂度而且数据这么大我们需要优化一下

对于dp[i][j-1] 因为只需要一行我们用滚动数组去存储就好了 对于dp[i - 1][k] 是一个数值用一个数存储就好了

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>using namespace std;
const int N = 1e6 + 10;int a[N];
int n, m;
int dp[10][10];
int d[N][2];int main()
{while(scanf("%d%d",&n,&m) != EOF){for(int i = 1; i <= m; ++ i)scanf("%d",&a[i]);memset(d,0,sizeof(d)); // int ans = -N;/*for(int i = 1; i <= n; ++ i)for(int j = i; j <= m; ++ j){for(int k = 1; k < j; ++ k)dp[i][j] = max(dp[i - 1][k] + a[j], dp[i][j - 1] + a[j]);}cout << dp[n][m] << endl;for(int i = 1; i <= n; ++ i){for(int j = 1; j <= m; ++ j)cout << dp[i][j] <<" ";cout << endl;}*/int maxv;for(int i = 1; i <= n; i++){maxv = - 0x3f3f3f3f;for(int j = i; j <= m; j ++){d[j][1] = max(d[j - 1][1] + a[j],d[j - 1][0] + a[j]);d[j - 1][0] = maxv;maxv = max(maxv,d[j][1]);}}printf("%d\n",maxv);}
}


题目大意:给你几种石头的规格然后问你石头最高可以堆多高一个石头可以放在另一个石头上面当且仅当下面石头接触面的大小严格大于上面的石头的接触面

思路有点像背包问题,我们可以将石头的每一个面都拆出来,经观察可知每个石头最多被选一次,对于每个面我们可以选和不选

设状态转移方程为dp[N][2];dp[N][1]表示选,dp[N][0]表示不选,那么表示就是我要是选这个的话max(我可以接到前面哪个石头堆上去)要是不选的话就是前面的石头堆里面的最大值

状态转移要是选的话dp[N][1] = max(dp[0 ~ N - 1][1],dp[0 ~ N - 1][0]) + h(该石头高度)

要是不选的话dp[N][0] = max(dp[0~N - 1][1],dp[0~N - 1][0]);

事先要对边进行排序

#include <iostream>
#include <map>
#include <algorithm>
#include <cstring>
#include <cstdio>#define f first
#define s second
using namespace std;
typedef pair<int,int> PII;
const int N = 30;map <PII,int> m;
PII p[N * N];
int dp[N * N][2];
int n;
int x, y, z;
inline void chang(int &x, int &y, int &z)
{if(x > y) swap(x,y);if(y > z) swap(y,z);if(x > z) swap(x,z);if(x > y) swap(x,y);
}
int main()
{int T = 1;   while(scanf("%d",&n) && n){int idx = 0;memset(dp,0,sizeof(dp));
//........................................................................for(int i = 0; i < n; ++ i){cin >> x >> y >> z;chang(x,y,z);p[idx ++] = {x,y}; m[p[idx - 1]] = z;p[idx ++] = {y,z}; m[p[idx - 1]] = x;p[idx ++] = {x,z}; m[p[idx - 1]] = y;}sort(p,p + idx);
//.......................................................................// for(int i = 0; i < idx; ++ i)//  cout << p[i].f << " " <<p[i].s <<" " << m[p[i]] <<" " << i <<endl;//  cout << endl;for(int i = 0; i < idx; ++ i){dp[i][1] = m[p[i]];for(int j = 0; j < i; ++ j){dp[i][0] = max(max(dp[j][1],dp[j][0]),dp[i][0]);if(p[i].f > p[j].f && p[i].s > p[j].s)dp[i][1] = max(dp[j][1] + m[p[i]],dp[i][1]);}}
//........................................................................// for(int i = 0; i < idx; ++ i)// {//     for(int j = 0; j < 2; ++ j)//       cout << dp[i][j] <<" ";//       cout << i << endl;// }printf("Case %d: maximum height = %d\n",T ++, max(dp[idx - 1][0],dp[idx - 1][1])); }return 0;
}


状态压缩dp(没想到)

题目大意给你各科的完成的时间以及ddl如果你的作业不能在规定时间内完成就会扣分求最小扣多少分

有点求哈曼顿最短路的味道(走过每一个求花费最小)

由于要记录路径所以,我们不能简单的求时长我们要记录->这个状是由哪些状态转移过来的,还要存储花费的时间

定义两个结构体一个存储输入信息,另一个记录到达当前状态的罚时,以及前面的状态是谁,和当前到了哪个状态

#include <iostream>
#include <string>
#include <cstring>
#include <stack>
#include <algorithm>
using namespace std;const int inf = 1 << 30;struct node
{string name;int dead, cost;
} a[50];struct kode
{int time, score, pre, now;
} dp[1 << 15];//表示当前状态的一些内容

第一重循环全排列所有状态

首先初始化当前状态的罚时为INF

第二重循环判断哪些位置有1,有1说明作业已经做了

如果给位置上有1,把1去掉就是你前面是由哪个状态转移过来的

计算一下转移花费的时间

最后的状态就是所有的数位都为1最后倒着输出就可以了

end = 1 << n;//全排列所有的情况数for (s = 1; s < end; s++)//枚举每一种状态,最后就是所有作业都写完了{dp[s].score = inf;for (i = n - 1; i >= 0; i--){int tem = 1 << i;if (s & tem){//cout<<s<<"      "<<tem<<endl;//这里这一步注释的可以用来解释 s & tem 的含义int past = s - tem;int st = dp[past].time + a[i].cost - a[i].dead;//前一个状态所花的时间与当前状态时间上的一些处理if (st<0)   st = 0;//如果小于0是不是说明没有超过截止时间?那么我们就没有扣分,所以为0if (st + dp[past].score<dp[s].score)//这个在循环中更新,跳出第二重循环找到的就是最优解{dp[s].score = st + dp[past].score;dp[s].now = i;//当前节点dp[s].pre = past;//前一个状态的记录dp[s].time = dp[past].time + a[i].cost;//时间的更新}}}}

下面是全部代码

#include <iostream>
#include <string>
#include <cstring>
#include <stack>
#include <algorithm>
using namespace std;const int inf = 1 << 30;struct node
{string name;int dead, cost;
} a[50];struct kode
{int time, score, pre, now;
} dp[1 << 15];//表示当前状态的一些内容int main()
{int t, i, j, s, n, end;cin >> t;while (t--){memset(dp, 0, sizeof(dp));cin >> n;for (i = 0; i<n; i++)cin >> a[i].name >> a[i].dead >> a[i].cost;end = 1 << n;//全排列所有的情况数for (s = 1; s < end; s++)//枚举每一种状态,最后就是所有作业都写完了{dp[s].score = inf;for (i = n - 1; i >= 0; i--){int tem = 1 << i;if (s & tem){//cout<<s<<"      "<<tem<<endl;//这里这一步注释的可以用来解释 s & tem 的含义int past = s - tem;int st = dp[past].time + a[i].cost - a[i].dead;//前一个状态所花的时间与当前状态时间上的一些处理if (st<0)   st = 0;//如果小于0是不是说明没有超过截止时间?那么我们就没有扣分,所以为0if (st + dp[past].score<dp[s].score)//这个在循环中更新,跳出第二重循环找到的就是最优解{dp[s].score = st + dp[past].score;dp[s].now = i;//当前节点dp[s].pre = past;//前一个状态的记录dp[s].time = dp[past].time + a[i].cost;//时间的更新}}}}stack<int> S;int tem = end - 1;cout << dp[tem].score << endl;//输出最少扣分while (tem){S.push(dp[tem].now);//利用栈先进后出的特点tem = dp[tem].pre;//为了迭代进行所以这样更新}while (!S.empty()){cout << a[S.top()].name << endl;S.pop();}}return 0;
}

逆推形dp

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <map>
#include <cstring>
#define f first
#define s second
using namespace std;
typedef long long LL;
typedef pair <int,int> PII;
const int N = 1e5 + 5;
int gg[N][15];
int dp[N][15];
//第i秒你在j这个位置能接到饼的最大值
int main()
{int T;while(scanf("%d",&T), T){int maxv = 0;memset(dp,0,sizeof(dp));memset(gg,0,sizeof(gg));for(int i = 0; i < T; ++ i){int  x, t;scanf("%d%d",&x,&t);maxv = max(maxv,t);x ++;//为了不处理边界情况直接变成(1 ~ 11)gg[t][x] ++;}//  dp[i][j] = max(dp[i - 1][j - 1],dp[i - 1][j + 1],dp[i - 1][j]) + mp[{j,i}]for(int i = maxv; i >= 0; -- i)for(int j = 1; j <= 11; ++ j){dp[i][j]=max(dp[i+1][j],max(dp[i+1][j-1],dp[i+1][j+1]));dp[i][j] += gg[i][j];}printf("%d\n",dp[0][6]);}return 0;
}


动态规划,从下往上找,dp[i][2]中dp[i][0]表示第i个平台最左边到底的最短时间,dp[i][1]表示平台最右边到底的最短时间。

状态转移方程:dp[i][1]=a[i].h-a[k].h+min(dp[k][0]+a[i].x2-a[k].x1,dp[k][1]+a[k].x2-a[i].x2);//右

dp[i][0]=a[i].h-a[k].h+min(dp[k][0]+a[i].x1-a[k].x1,dp[k][1]+a[k].x2-a[i].x1); //左

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <numeric>
const int INF=0x3f3f3f;//无穷大
using namespace std;
typedef long long ll;
int T;
int N,X,Y,MAXH;
//0表示第i个平台最左边到底的最短时间
//1表示第i个平台最右边到底的最短时间
int dp[1010][2];
struct node
{int x1,x2,h;
};
bool cmp(node a,node b)//根据h从大到小排列
{return a.h>b.h;
}
node a[1010];
void LeftTime(int i)//左
{int k=i+1;while(k<N+1&&a[i].h-a[k].h<=MAXH){if(a[i].x1>=a[k].x1&&a[i].x1<=a[k].x2){dp[i][0]=a[i].h-a[k].h+min(dp[k][0]+a[i].x1-a[k].x1,dp[k][1]+a[k].x2-a[i].x1);return;}k++;}if(a[i].h-a[k].h>MAXH)//不能到达下一平台dp[i][0]=INF;else//直接落地dp[i][0]=a[i].h;return;
}
void RightTime(int i)//右
{int k=i+1;while(k<N+1&&a[i].h-a[k].h<=MAXH){if(a[i].x2>=a[k].x1&&a[i].x2<=a[k].x2){dp[i][1]=a[i].h-a[k].h+min(dp[k][0]+a[i].x2-a[k].x1,dp[k][1]+a[k].x2-a[i].x2);return;}k++;}if(a[i].h-a[k].h>MAXH)//不能到达下一平台dp[i][1]=INF;else//直接落地dp[i][1]=a[i].h;return;
}
int main()
{cin >> T;while(T--){memset(dp,0,sizeof(dp));a[0].x1=-20000,a[0].x2=20000,a[0].h=0;//大地cin >> N>>X>>Y>>MAXH;a[1].x1=X,a[1].x2=X,a[1].h=Y;//初始位置for(int i=2; i<=N+1; i++){cin >> a[i].x1 >> a[i].x2 >> a[i].h;}sort(a,a+N+2,cmp);for(int i=N; i>=0; i--){LeftTime(i);//左RightTime(i);//右}int MinTime=min(dp[0][0],dp[0][1]);cout << MinTime <<endl;}return 0;
}

区间dp


题目大意:给你n个数字v(1),v(2),…,v(n-1),v(n),每次你可以取出最左端的数字或者取出最右端的数字,一共取n次取完。假设你第i次取的数字是x,你可以获得i*x的价值。你需要规划取数顺序,使获得的总价值之和最大

思路:区间dp

因为你取得顺序步一样就会产生不同的序列,对于不同的序列再分割,从子结构最优到整体最优

dp[i][j] 代表从i取到j的最大总数

dp[i][j] = max(dp[i+1][j]+a[i](n+i-j) , dp[i][j-1]+a[j](n+i-j)) 即取右边的数 取左边的数 比较哪个大

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#define Mod 1000000007
#define eps 1e-6
#define ll long long
#define INF 0x3f3f3f3f
#define MEM(x, y) memset(x, y, sizeof(x))
#define Maxn 2000+10
using namespace std;
int n;
int dp[Maxn][Maxn],a[Maxn];
int main()
{MEM(dp,0);MEM(a,0);cin>>n;for(int i=0;i<n;i++)cin>>a[i];for(int len=0;len<n;len++)//区间长度len{for(int i=0;i+len<n;i++)//固定区间左边起点{int l=i,r=i+len;//区间左、右点//取右边的数   取左边的数  比较哪个大dp[l][r]=max(dp[l+1][r]+a[l]*(n+l-r),dp[l][r-1]+a[r]*(n+l-r));}}cout<<dp[0][n-1]<<endl;return 0;}

绝对值类型dp


题目大意:农夫约翰想改造一条路,原来的路的每一段海拔是A_i,修理后是B_i,花费|A_i – B_i|。我们要求修好的路是单调不升或者单调不降的。求最小花费。

思路:可以这样设计DP, d[i][j]表示第i个数变成j的最优解, 这样它转移到d[i-1][k], 其中k<=j, 这是变成上升的, 代价是abs(a[i] - j)。 但是数太大了, 又因为每个数肯定会变成这些数中的一个数会最优, 所以我们不妨将n个数先离散化一下, 这样状态就表示成d[i][j]表示第i个数变成第j小的数, 转移到d[i-1][k],其中k<=j。 但是这样还是超时了, 因为是三重循环, 又发现,每次都是取前一层的当前最小值, 所以很容易将第3层优化掉。

感觉还是比较难的qwq证明还是不会的

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int MAXN = 2333;
const int inf = 1e9;
int n;
int dp[MAXN],a[MAXN],b[MAXN],num[MAXN];int get_ans(int *a,int *num)
{memset(dp,0,sizeof dp);for(int i = 1 ; i <= n ; ++i){int MIN = inf;for(int j = 1 ; j <= n ; ++j){MIN = min(MIN,dp[j]);dp[j] = MIN + abs(a[i] - num[j]);}}int ans = inf;for(int i = 1 ; i <= n ; ++i)ans = min(ans,dp[i]);return ans;
}
int main()
{scanf("%d",&n);for(int i = 1 ; i <= n ;++i){scanf("%d",&a[i]);num[i] = a[i];b[n-i+1] = a[i];}sort(num+1,num+1+n);printf("%d\n",min(get_ans(a,num),get_ans(b,num)));return 0;
}

持续更新------------------------------------------------------------------------------------------------

【Kuangbin 带你飞系列】 基础dp相关推荐

  1. [kuangbin带你飞]专题十二 基础DP1 题解+总结

    kuangbin带你飞:点击进入新世界 总结: 简单dp,最近在做,持续更新. 文章目录 总结: 1.Max Sum Plus Plus 2.Ignatius and the Princess IV ...

  2. “kuangbin带你飞”专题计划——专题十四:数论基础

    写在前面 1.目前还没啥写的.开始时间:2021-05-13(其实博客上看得到该博客创建时间的) 2.上一个专题刷的是网络流(博客总结),属于第一次接触.本来想的是一周特别高效,然后一周略划水,结果是 ...

  3. (2021-07-14~)“kuangbin带你飞”专题计划——专题十三:基础计算几何

    目录 前言 参考博客 自己总结的东西: 难度判断? 题目 1.[TOYS POJ - 2318 ](解决) 2.[Toy Storage POJ - 2398 ](解决) 3.[Segments PO ...

  4. kuangbin带你飞专题合集

    题目列表 [kuangbin带你飞]专题一 简单搜索 [kuangbin带你飞]专题二 搜索进阶 [kuangbin带你飞]专题三 Dancing Links [kuangbin带你飞]专题四 最短路 ...

  5. kuangbin带你飞 专题1-23 题单

    kuangbin大神,对于打过ACM比赛的ACMer,无人不知无人不晓. 在此,附上vjudge平台上一位大神整理的[kuangbin带你飞]专题目录链接. [kuangbin带你飞专题目录1-23] ...

  6. 解题报告:【kuangbin带你飞】专题四 最短路练习题

    目录 A. POJ - 2387 TiltheCowsComeHomeTil\ the\ Cows\ Come\ HomeTil the Cows Come Home--------(最短路模板题)[ ...

  7. [kuangbin带你飞]专题五 并查集 题解+总结

    kuangbin带你飞:点击进入新世界 总结: 本人算是初学者中的初学者,欢迎交流~ 并查集的接触过的不多,大概只有普通并查集,带权并查集,种族并查集,传说中的可持续化并查集只是听说过还没有接触,不过 ...

  8. 线段树开新坑:kuangbin带你飞

    写在最前面的废话 这里I以前的题是暑假刚刚开始的时候在家写的,然后多校一波就荒废了 9月开头回家一波,重新填坑,= =,kuangbin带你飞的pdf,这才一半题,后面还有一波,蓝瘦,慢慢写吧,不写题 ...

  9. 【kuangbin带你飞】专题六 最小生成树

    [kuangbin带你飞]专题六 最小生成树 A.POJ - 1251 Jungle Roads (最小生成树模板) The Head Elder of the tropical island of ...

最新文章

  1. 人工智能浪潮褪去,冲刺IPO成AI企业生存关键?
  2. python 高并发 select socket_socket + select 完成伪并发操作的实例
  3. Python基础概念_7_数据结构
  4. ICEfaces抄袭PrimeFaces
  5. create 添加async和不添加的区别_鸽子饮水添加剂肝精与电解质的区别,不能混淆也不能代替...
  6. java 管理后台前台分离_系统前台后台是否应该分离(包括部署)
  7. C# 连蒙带骗不知所以然的搞定USB下位机读写
  8. 刘德华--6雪藏是一种代价
  9. Ubuntu20.04之安装搜狗输入法
  10. 用python简单代码做一个计算器
  11. 用python批量发送短信_秒嘀云发送短信Python实现
  12. SageMath使用
  13. 三国志战略版:Daniel_兵无常势分析
  14. 看这玩意复习你还会挂科?《数据结构篇》
  15. 做一个商业网站要多少钱?
  16. BZOJ 3162: 独钓寒江雪 树的同构 + 组合 + 计数
  17. 有没有html代码听力的软件吗,英语听力软件哪个好?2017英语听力软件排行榜
  18. USBwriter后恢复U盘
  19. App Store 4.3 大礼包被拒 ,马甲包制作方案
  20. php c语言在线编译器,在线C语言编译器/解释器

热门文章

  1. MySQL WindowsCMD常用命令!
  2. ACMNO.2 输入一个华氏温度,要求输出摄氏温度。公式为 c=5(F-32)/9 输出要求有文字说明,取位2小数。 输入 一个华氏温度,浮点数 输出 摄氏温度,浮点两位小数
  3. 自动驾驶汽车可用于处理急转弯的 3 种技术
  4. 暑假想打比赛,小白怎么从0入门?
  5. webuploader自己造的坑
  6. Date, TimeZone, MongoDB, java中date的时区问题
  7. 20道常见初级Java面试题
  8. app开发外包的流程、需求、报价,需要知道的细节!
  9. SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因...
  10. 嵌入式Linux内存压力测试