动态规划

递推  递归   记忆化搜索

斐波那契数列

1、用其他已经计算好的结果计算自己的结果(递推)

2、用自己的值计算别人的值(考虑对之后的项做出的贡献)

cin >> n;
f[]=;f[]=; for (int a=;a<=n;a++)
f[a] = f[a-] + f[a-]; for (int a=;a<n;a++)
{
f[a+] += f[a];
f[a+] += f[a];
}

理论上两种方法都是可以的,但有的题一种方法会很难写,另一种方法就很好写,所以两种都需要掌握

3、记忆化搜索

递归处理斐波那契数列的第n项,找到边界,遇到第一项返回1,遇到第2项返回0。

这样的话复杂度是O(f[n])的,因为他是由一个一个1累加起来的

接近(2^n)

慢的原因是我们把很多项重复算了很多次

我们考虑把已经算出来的项存下来,之后直接调用就行了

bool g[];

int dfs(int n)
{
if (n==) return ;
if (n==) return ;
if (g[n]) return f[n];
f[n] = dfs(n-) + dfs(n-);
g[n]=true;
return f[n];
}

无后效性:动态规划的所有状态之间组成了一个有向无环图

如果出现乱序转移的情况,就考虑拓扑排序(把所有的边变成从前往后),之后for一遍就行了

阶段性:

转移方程:怎么算

状态:要算的东西

背包

P1  N个物品,M的容积,每个物品有体积和价值,最大化价值和

采药

第一个维度:现在放了多少个物品,第二个维度:用了多少体积

设f[i][j]表示已经试到了第i个物品(第i个物品之前的都有可能放或者不放,但是编号都小于等于i),已经放进去的体积之和为j  这种情况下所取得的最大价值

那么第i+1个物品有两种情况:放或者不放

如果不放,f[i+1][j]=f[i][j]

如果放,f[i+1][j+v[i+1]]=f[i][j]+w[i+1]

这个方程是自己更新别人

如果用别人更新自己

不放:f[i][j]=f[i-1][j]

放:f[i][j]=f[i-1][j-v[i]]+w[i]

直接取max

int n,m,w[],v[];
int f[][]; int main()
{
cin >> n >> m;
for (int a=;a<=n;a++)
cin >> v[a] >> w[a];
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
{
f[i][j] = f[i-][j];
if (j >= v[i]) f[i][j] = max(f[i][j],f[i-][j-v[i]]+w[i]);
}
int ans=;
for (int a=;a<=m;a++)
ans = max(ans,f[n][a]);
cout << ans << endl;
return ;
}

记得加判断,最后要在所有的体积的价值中取max

P2  每个物品可以用无限次

枚举第i个物品到底放了多少个

直接枚举复杂度会炸掉

其实这个时候直接从自己转移过来就行了

f[i][j]=f[i-1][j-v[i]]+w[i]把i-1换成i,相当于是枚举了,但是复杂度会降下来O(nm)

#include<iostream>

using namespace std;

int n,m,w[],v[];
int f[][]; int main()
{
cin >> n >> m;
for (int a=;a<=n;a++)
cin >> v[a] >> w[a];
/*for (int i=1;i<=n;i++)
for (int j=0;j<=m;j++)
for (int k=0;k*v[i]<=j;k++)
f[i][j] = max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);*/
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
{
f[i][j] = f[i-][j];
if (j >= v[i]) f[i][j] = max(f[i][j],f[i][j-v[i]]+w[i]);
}
int ans=;
for (int a=;a<=m;a++)
ans = max(ans,f[n][a]);
cout << ans << endl;
return ;
}

P3  有限背包  每个物品可以用无限次

直接枚举第i个物品用多少次

复杂度将近O(n^3)

如何优化?

直接造物品

比如有一个可以用13次,体积为v[i]的物品

我们把它分成只可以用一次的体积为v[i],2v[i],4v[i],6v[i]的物品,那么这和原问题是等价的

问题就变成了0/1背包

复杂度O(nmk)(k表示分成了多少个小的捆绑包)

怎么分呢?

先按照二进制从小到大一个一个拆,直到不足够下一个二进制了就把剩下的单独拆出来

比如11=2^0+2^1+2^2+6

K≈log(n)

复杂度也就是O(nmlogn)

证明:比如31=2^0+2^1+2^2+2^3+2^4

37=31+6

就ok了

#include<iostream>

using namespace std;

int n,m,w[],v[];
int f[][]; int main()
{
cin >> n >> m;
int cnt = ;
for (int a=;a<=n;a++)
{
int v_,w_,z;
cin >> v_>> w_ >> z; int x = ;
while (x <= z)
{
cnt ++;
v[cnt] = v_*x;
w[cnt] = w_*x;
z-=x;
x*=;
}
if (z>)
{
cnt ++;
v[cnt] = v_*z;
w[cnt] = w_*z;
}
}
n=cnt;
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
{
f[i][j] = f[i-][j];
if (j >= v[i]) f[i][j] = max(f[i][j],f[i-][j-v[i]]+w[i]);
}
int ans=;
for (int a=;a<=m;a++)
ans = max(ans,f[n][a]);
cout << ans << endl;
return ;
}

基础类dp

例 数字三角形

每个位置可以由上方或者左上方得到

f[i][j]=max(f[i-1][j-1],f[i-1][j])+a[i][j]

数字三角形2

我们发现前面的和模100时大,但是后面不一定大

考虑加一维

定义bool数组f[i][j][k]表示走到[i][j]时模100为k是否可能

转移方程:f[i][j][k]=f[i-1][j-1][(k-a[i][j])%100]||f[i-1][j][(k-a[i][j])%100]
边界f[1][1][a[1][1]%100]=true
然后找到一个最大的k

转移的时候用自己的值更新其他的值就可以了

dp要注意加维度

#include<iostream>

using namespace std;

bool f[][][];

int main()
{
cin >> n;
for (int i=;i<=n;i++)
for (int j=;j<=i;j++)
cin >> a[i][j]; f[][][a[][] % ] = true;
for (int i=;i<n;i++)
for (int j=;j<=i;j++)
for (int k=;k<;k++)
if (f[i][j][k])
{
f[i+][j][(k+a[i+][j])%]=true;
f[i+][j+][(k+a[i+][j+])%]=true;
} for (int j=;j<=n;j++)
for (int k=;k<;k++)
if (f[n][j][k]) ans=max(ans,k);
cout << ans << endl; return ;
}

最长上升子序列

f[i]=max(f[j])(1<=j<i)+1

复杂度O(n^2)

当数据范围10^5时就线段树或者平衡树维护一下就行了

dp有的时候可以用数据结构优化

区间dp

例:合并石子

我们发现合并的话一定是把相邻的区间合并成一堆石子

比如把1和4合并,2和3肯定已经被合并过了

满足只能合并相邻的两个东西,这样的一定是区间dp

状态:f[l][r]表示把第l堆石子和第r堆石子合并的最小代价是多少

考虑在a[l]到a[r]之间找到一条分界线,先把左边的合并,再把右边的合并,再把这两堆合并

f[l][r]=min(f[l][p]+f[p+1][r])+sum[r]-sum[l-1] (l<=p<r)

最后求得答案就是f[1][n]

最直观的想法:枚举l,r,p

但是这样是不对的

比如枚举[1,n]中的p=2,我们发现f[3][n]还没有算出来,那就gg了

所以我们应该第一维枚举长度,第二维枚举左右端点,第三维枚举端点就好了

for (int a=;a<=n;a++)
{
cin >> z[a];
sum[a] = sum[a-]+z[a];
}
memset(f,0x3f,sizeof(f));
for (int a=;a<=n;a++)
f[a][a] = ;
for (int len=;len<=n;len++)
for (int l=,r=len;r<=n;l++,r++)
for (int p=l;p<r;p++)
f[l][r] = min(f[l][r],f[l][p]+f[p+][r]+sum[r]-sum[l-]);
cout << f[][n] << endl;

矩阵乘法,调整运算次序使得运算次数最小

最后是把n个矩阵合并成1个矩阵

每次合并相邻两个矩阵

f[l][r]表示把l~r的矩阵合并成一个矩阵所需要的最小次数

还是枚举断点

f[l][r]=min(f[l][p]+f[p+1][r])+a[l]*a[p+1]*a[r+1]  (l<=p<r)

按照石子合并的方式搞一搞就行了

状压dp

平面上有n个点,第i个点的坐标为(x[i],y[i])。有一个人刚开始在一号点,想让他走完其他的点再回到原点,问你最短的距离是多少

变化量:我现在在哪个点,我已经走过了哪些点

f[s][i]表示我走过了哪些点,现在在第i个点

状态压缩:把一个数组压缩成一个数。

哪个点经过了就把他对应的二进制的位数变成1

转移:枚举j,当二进制位第j位为0时就可以考虑转移

除了f[1][0]=0,其他初始化为1

Dp完之后再循环一遍找答案

要先枚举s在

复杂度O(2^n * n^2)

一般来说状压dp解决的范围在n<=22或n<=20

double f[][];
double x[],y[]; int main()
{
cin >> n;
for (int a=;a<n;a++)
cin >> x[a] >> y[a];
f=∞
f[][]=;
for (int s=;s<(<<n);s++)
for (int i=;i<n;i++)
if (f[s][i] < ∞)
{
for (int j=;j<n;j++)
if ( ((s>>j) & ) == )
{
int news = s | (<<j);
f[news][j] = min(f[news][j],f[s][i] + dis(i,j));
}
}
for (int i=;i<n;i++)
ans=min(ans, f[(<<n)-][i] + dis(i,)); return ;
}

qbzt day5 上午的更多相关文章

  1. Day5上午解题报告

    预计分数:100+40+30=170 实际假分数:0+0+0=0 CE*3 实际真分数:60+50+0=110 老师没把我的程序放的文件夹里面,于是..... T1 https://www.luogu ...

  2. qbzt day7上午

    由于优盘咕咕咕了,所以这篇就咕咕咕了 以后还会补上的 qwq

  3. qbzt day6 上午

    还是合并石子,但是这次可以任意两个合并,并且求最大异或和 f[s]表示把s所对应的的石子合并为一堆的最小代价 最后求f[2^n-1] 怎么转移? 最后一次也是把两堆合并成一堆,但是会有很多情况,可以枚 ...

  4. qbzt day5 下午

    农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地.John打算在牧场上的某几格里种上美味的草,供他的奶牛们享 ...

  5. qbzt day4 上午

    图论 最短路:dijkstra   spfa   floyd 最小生成树:kruskal 连通性:bfs/dfs    tarjan(强连通分量) 其它:拓扑排序    LCA 齿轮: 图的dfs树只 ...

  6. qbzt day3 上午

    内容提要 堆 lca(最近公共祖先) st表 hash 并查集 树状数组 线段树 数据结构 1.堆 Priority_queue 他滋兹:插入删除查询最大值(最小值) 分为大根堆小根堆 2.LCA 首 ...

  7. qbzt day2 上午

    内容提要 贪心 分治 分块 搜索 接着昨天的讲 过河问题 考虑AB是最快的人,CD是最慢的人,要把CD两个人送过河,只有两种方案,牵扯到四个人,并且n个规模的原问题化成了n-2个规模的子问题 那么最后 ...

  8. qbzt day1 上午

    内容提要 模拟,贪心 在讲这些东西之前,我们先来了解一个东西:high level 这个东西大体上就是你做题之前要先想清楚自己要写什么,怎么写,然后再写,不要有一点写一点 1.模拟 模拟算法算是很水的 ...

  9. WC2015流水账

    THU那四场考试没考好,只有20+名.这也许是我OI生涯中最后一场吧(已确认是最后一场),真是感慨万千. day0 搬进浙大宿舍404房间(神房间号),四个人一间.中午发现学军伙食相当良心,是我参加的 ...

随机推荐

  1. 分布式事务——幂等设计(rocketmq案例)

    幂等指的就是执行多次和执行一次的效果相同,主要是为了防止数据重复消费.MQ中为了保证消息的可靠性,生产者发送消息失败(例如网络超时)会触发 "重试机制",它不是生产者重试而是MQ自 ...

  2. 我国三大常用坐标系:北京54、西安80和WGS-84

    转自:http://blog.sina.com.cn/s/blog_6dbe2d780100mwr5.html 我国三大常用坐标系:北京54.西安80和WGS-84 1.北京54坐标系(BJZ54)北 ...

  3. Windows系统下安装MySQL详细教程(命令安装法)

    1.安装包下载. 下载地址:https://dev.mysql.com/downloads/mysql/ 点击下载之后,可以选择注册Oracle账号,也可以跳过直接下载. 下载完成后,选择一个磁盘内放 ...

  4. http参数传递方式

    url传参 这种在各种method(get,post,delete,put)都能使用,解析速度快 body体中的参数 application/x-www-form-urlencoded 这应该是最常见 ...

  5. Codeforces1214D. Treasure Island (dp + Hash)

    题目链接:传送门 思路: 仔细观察可以发现,答案最多就是2,只要把(2,1)和(1,2)堵住就可以了. 答案是0的情况就是初始状态下,(1,1)就已经不可达(n,m)了,很好判断. 所以重点就是区分答 ...

  6. tr 替换或删除字符

    1.命令功能 tr 从标准输入中替换,压缩间隔或者删除字符并从定向到标准输出. 2.语法格式 tr  option  SET1  SET2 参数 参数说明 -c 取代所有SET1中字符串 -d 删除所 ...

  7. 什么原因?全球许多网络提供商推迟部署IPv6

    全球许多网络提供商推迟部署IPv6,指出升级路由器和交换机的成本以及NAT为扩展IPv4地址所取得的令人印象深刻的成就. 这并没有阻止像澳门CTM这样的互联网服务供应商不要冒险,以此为榜样,并且满足终 ...

  8. wepy-开发总结(功能点)

    开发小程序中,遇到的wepy的几点坑,记录一下; 更详细的项目总结记录请见我的个人博客:https://fanghongliang.github.io/ 1.定时器: 在页面中有需要用到倒计时或者其他 ...

  9. 【Luogu5293】[HNOI2019] 白兔之舞

    题目链接 题目描述 略 Sol 考场上暴力 \(O(L)\) 50分真良心. 简单的推一下式子,对于一个 t 来说,答案就是: \[\sum_{i=0}^{L} [k|(i-t)] {L\choose ...

  10. Jackson常用工具类

    原创转载请注明出处:https://www.cnblogs.com/agilestyle/p/11983194.html Demo package org.fool.util; import com. ...