白书上的DP讲义: 

DAG上的dp 不要好高骛远去学这种高端东西,学了也写不对,剩下的几天把基本的dp和搜索搞下,就圆满了。不要再学新算法了,去九度把现有的算法写个痛。


学了数位DP和记忆搜索,今天遇到一道标准的水题,搞了三小时还没搞出来真是讽刺啊:

座位问题

分析:

1.在任何时候,一个女生的左边或者右边至少有一个女生,这就是说若只有1个人,则这个人必须为男生。(竟然一直没意识到这点,我能说我要是看明白了这一行,就能ac吗?)

2.有n个人的排列,则应该从第n个人开始往第一个人推。这样思路就比较明确:不用去管第一个人究竟是男生还是女生,只用根据第n个人的性别,往前推。

第n个人可以是男生也可以是女生:总的排列数 = 第n个人是男生的情况 + 第n个人是女生的情况。

用dp[n][0]表示,有n个人的排列,排在第n个的是女生。用dp[n][1]表示,排在第n个的是女生

则: dp[n][0]应该加上dp[n-1][0],因为若n-1个人的排列中第n-1个为女生,则外面还可以再坐一个女生。还应加上dp[n-2][1],因为若第n-2个人为男生,则可以再坐2个女生。

#include <cstdio>

const int MOD = ;
const int MAXN = ;
int dp[MAXN][];
//dp[i][0] i个人,第一个为女生
//dp[i][1] i个人,第一个为男生 void init() {
//只有1个人时,必须为男生
dp[][] = ;
dp[][] = ;
dp[][] = ;
dp[][] = ;
for (int i = ; i < MAXN; ++i) {
//若最前面为女生,则前面还可以坐一个女生,
//最前面为男生,则可以做2个女生
dp[i][] = (dp[i - ][] + dp[i - ][]) % MOD;
dp[i][] = (dp[i - ][] + dp[i - ][]) % MOD;
}
} int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int n;
init();
while (scanf("%d", &n) != EOF) {
printf("%d\n", (dp[n][] + dp[n][]) % MOD);
}
return ;
}

自己用记忆搜索搞的:

#include <cstdio>
#include <memory.h> const int MOD = ;
int dp[][]; int dfs(int x, int t) { //第一个是男 || 女
if (x == && t == ) return ; // <--------------1个人只可能是男
if (x == && t == ) return ;
if (x == ) return ;
if (dp[x][t] != ) return dp[x][t];
long long ret = ;
if (t == ) { //前一个是女
ret += dfs(x - , );
ret += dfs(x - , ); //
} else {
ret += dfs(x - , );
ret += dfs(x - , );
}
dp[x][t] = ret % MOD;
return dp[x][t];
} int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int n;
while (scanf("%d", &n) != EOF) {
if (n == ) {
puts("");
continue;
}
memset(dp, , sizeof dp);
printf("%d\n", (dfs(n, ) + dfs(n, )) % MOD);
}
return ;
}

实际上ret不必要用long long,2 * MOD < 2 ^ 31 - 1。

最长递增子序列:今天遇到2道这种题,都写错了。突然想起来这不是王道八套模拟上面的原题?当时是逐项相减变成最长连续子序列和的问题。现在竟然不会了。

传统的dp算法O(n^2)数据大于10000时会超时,故需要改进:不用动态规划的方法。

维护一个数组dp[n],令dp[len]表示长度为len的子序列的最末尾的数字的最小值:(勉强想明白过程,还没想好该怎么表达)

1.由更新规则知:dp[i]严格递增,dp[2] > dp[1]必然成立。

2.对于每个新的数字k,必能找到一个长度为i的序列,使得其最后面的数字为k,这时就要更新dp[1...n]数组,使得dp[i] = k,那么

3.

#include <cstdio>

const int MAXN = ;

inline int Max(int a, int b) {
return a > b ? a : b;
} int len;
int dp[MAXN];
//查找下界
int find(int x) {
int left = ;
int right = len;
while (left < right) {
int mid = left + (right - left) / ;
if(dp[mid] >= x) right = mid;
else left = mid+;
}
return left;
} int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int n;
while (scanf("%d", &n) != EOF) {
int num[MAXN];
for (int i = ; i <= n; ++i) scanf("%d", &num[i]);
len = ;
dp[len] = num[];
for (int i = ; i <= n; ++i) {
if (num[i] > dp[len]) {
dp[++len] = num[i];
} else {//更新
int pos = find(num[i]);
dp[pos] = num[i];
}
}
printf("%d\n", len);
}
return ;
}

首尾相连数组的最大子数组和

一看到这题我就瞬间定式思维去循环扫描的求最大子数组和,果断WA。

对于这样的一组数据:1 10 -4 -5 5 5  若循环扫描求最大子数组和,就会计算为: 1+....+5 = 12。实际结果是21,除去中间的-4,-5。

因为lcs的dp状态转移方程为: dp[i] = max(dp[i - 1] + a[i], a[i]),最后再比较dp[i]找出最大值...对于循环数组,中间的-4,-5可以跳过去,从5开始计算到1,10。

所以应先计算:max1 = a[0] ...a[n]的最大子列和,然后计算max2 = a[i],…a[n-1],a[0],…,a[j]这种情况下的最大子列和,最终结果为max(max1, max2)。

max2的计算可以为:总的数组和 - 最小负数子列和(将整个数组取反后为最大正数子列和)。

#include <cstdio>

const int MAXN = ;

int lcs(int a[], int n) {
int sum = ;
int ret = ;
for (int i = ; i < n; ++i) {
if (sum > ) sum += a[i];
else sum = a[i];
if (sum > ret) ret = sum;
}
return ret;
} int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int n;
while (scanf("%d", &n) != EOF) {
int a[MAXN];
for (int i = ; i < n; ++i) scanf("%d", &a[i]);
//先求出连续数组的最大子序列和
int max1 = lcs(a, n);
int sum = ;
for (int i = ; i < n; ++i) { //将数组取反
sum += a[i];
a[i] = -a[i];
}
//再找出最小子序列和
int max2 = lcs(a, n);
max2 = sum + max2;
printf("%d\n", max1 > max2 ? max1 : max2);
} return ;
}

股票交易

这题贪心应该能ac,昨天贪心过了4个测试点。dp的方法需要优化一下,不然一个测试点就直接10 ^ 9超时。

状态转移方程:dp[i][j](表示前j天进行了i次交易),则

首先找出 1.2...k,k <= j - 1中哪一天买进然后第j天卖出时收益最大,第j天可以选择不卖或者卖出(第k天买进)

dp[i][j] = max(dp[i][j-1], dp[i-1][k] + a[j] - a[k]);令tmp = MAX(dp[i-1][k] + a[j] - a[k]),考虑到tmp 中dp[i-1][j]-a[k]只要保持1...j中的最大值就行,故不需要每次都单独循环1...j计算一下tmp。就能将三层循环优化到2层。

#include <cstdio>
#include <memory.h> const int MAXN = ; inline int Max(int a, int b) {
return a > b ? a : b;
}
int dp[MAXN][MAXN]; //前j天进行i次交易,的最大收益
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int n, k;
while (scanf("%d%d", &n, &k) != EOF) {
int a[MAXN];
for (int i = ; i <= n; ++i) scanf("%d", &a[i]);
memset(dp, , sizeof dp);
for (int i = ; i <= k; ++i) { //将三维dp优化到二维
int tmp = dp[i][] - a[]; // tmp = max(dp[i-1][k] - a[k]) K = 1...j-1
for (int j = ; j <= n; ++j) {
dp[i][j] = Max(tmp + a[j], dp[i][j]);
dp[i][j] = Max(dp[i][j - ], dp[i][j]);
if (dp[i - ][j] - a[j] > tmp) tmp = dp[i - ][j] - a[j];
}
}
printf("%d\n", dp[k][n]);
}
return ;
}

Jobdu MM分水果

dp解法:

#include <cstdio>
#include <memory.h> bool dp[]; int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int n;
int wi[];
while (scanf("%d", &n) != EOF) {
int total = ;
for (int i = ; i <= n; ++i) {
scanf("%d", &wi[i]);
total += wi[i];
}
memset(dp, false, sizeof dp);
int half = total / ;
dp[] = true;
for (int i = ; i <= n; ++i)
for (int j = half; j >= wi[i]; --j)
dp[j] = dp[j] || dp[j - wi[i]];
for (int i = half; i >= ; --i) {
if (dp[i]) {
printf("%d\n", total - * i);
break;
}
}
}
return ;
}

dfs剪枝:

#include <cstdio>
#include <algorithm> int half;
int ans;
int w[];
int sum[]; //找出<=half可以取到的最小值
void dfs(int i, int pack) {
if (pack > half || pack + sum[i] < ans) return; //把比当前值小的剪掉
if (pack > ans) ans = pack;
if (i == -) return;
if (ans == half) return;
dfs(i - , pack + w[i]);
dfs(i - , pack);
} int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int n;
while (scanf("%d", &n) != EOF) {
int total = ;
for (int i = ; i < n; ++i) {
scanf("%d", &w[i]);
total += w[i];
}
std::sort(w, w + n);
sum[] = w[];
for (int i = ; i < n; ++i) {
sum[i] = sum[i - ] + w[i];
}
half = total / ;
ans = ;
dfs(n - , );
printf("%d\n", total - * ans);
}
return ;
}

DP重新学的更多相关文章

  1. dp重拾-完全背包--HDU 4508

    减肥记 湫湫给了你每日食物清单,上面描述了当天她想吃的每种食物能带给她的幸福程度,以及会增加的卡路里量. Input 输入包含多组测试用例. 每组数据以一个整数n开始,表示每天的食物清单有n种食物. ...

  2. dp重拾-01背包--HDU 2602

    Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like ...

  3. 重新学struct,边界对齐,声明……与Union的区别

    在内存中,编译器按照成员列表顺序分别为每个结构体变量成员分配内存,当存储过程中需要满足边界对齐的要求时,编译器会在成员之间留下额外的内存空间. 如果想确认结构体占多少存储空间,则使用关键字sizeof ...

  4. 重新学pytorch

    安装: conda install pytorch -c pytorch pip3 install torchvision pip install torchtext 这3个命令就够了

  5. Mac网络命令 老命令重新学

    网络与通信操作 命令名 功能描述 使用举例 telnet 远程登录 telnet hpc.sp.net.edu.cn rlogin 远程登录 rlogin hostname -l username r ...

  6. gym100825G. Tray Bien(轮廓线DP)

    题意:3 * N的格子 有一些点是坏的 用1X1和1X2的砖铺有多少种方法 题解:重新学了下轮廓线 写的很舒服 #include <bits/stdc++.h> using namespa ...

  7. [SDOI2012]任务安排 - 斜率优化dp

    虽然以前学过斜率优化dp但是忘得和没学过一样了.就当是重新学了. 题意很简单(反人类),利用费用提前的思想,考虑这一次决策对当前以及对未来的贡献,设 \(f_i\) 为做完前 \(i\) 个任务的贡献 ...

  8. 打算写一个《重学Node.js》系列,希望大家多多支持

    先放上链接吧,项目已经开始2周了:https://github.com/hellozhangran/happy-egg-server 想法 现在是2019年11月24日,还有人要开始学习Node.js ...

  9. 「10.14」小P的2048(模拟)·小P的单调数列(性质,DP)·小P的生成树(乱搞)

    A. 小P的2048 模拟.....又没啥可说的,以后要认真打打模拟题了... B. 小P的单调数列 考场$n^2log(n)$的SB思路有人听吗 正解当然不是这样, 事实上我们每次选取的只有一段区间 ...

随机推荐

  1. 例题:for循环迭代法。一个棋盘有n个格子,第一个格子有一粒米,第二个格子有两粒米,第三个格子有四粒米,依次类推,第n个格子里有多少粒米,棋盘里一共有多少粒米。

    decimal a = 1;//定义初始值,decimal可以定义比较长的数值            decimal sum = 1;            Console.WriteLine(&qu ...

  2. linux笔记:RPM软件包管理-yum在线管理

    ip地址配置: 用ifconfig命令只能配置ip和子网掩码,这样只能访问内网:如果需要访问公网则还必须要网关和DNS. 使用setup工具配置ip: 网络yum源配置: 常用yum命令:查询 常用y ...

  3. linux笔记:压缩解压命令gzip,gunzip,tar,zip,unzip,bzip2,bunzip2

    命令名称:gzip功能:压缩文件命令所在路径:/bin/gzip用法:gzip 文件压缩后文件格式:.gz其他:压缩后不保留原文件:只能压缩文件,不能压缩目录 命令名称:gunzip功能:解压.gz格 ...

  4. IIS线程池与ASP.NET线程池

    原文地址:http://www.cnblogs.com/dudu/p/3762672.html 1. IIS线程池 W3 Thread Pool(W3TP) 当处于内核模式的http.sys接收到来自 ...

  5. bootstrap--input框选择日期

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  6. 221. Maximal Square -- 矩阵中1组成的最大正方形

    Given a 2D binary matrix filled with 0's and 1's, find the largest square containing only 1's and re ...

  7. iOS 开发 – 均衡代码职责

    前言 文章的标题有点绕口,不过想了半天,想不到更好的标题了.本文的诞生有一部分功劳要归于iOS应用现状分析,标题也是来源于原文中的"能把代码职责均衡的划分到不同的功能类里".如果你 ...

  8. IDEA 创建Java Web项目

    发现项目目录没有classes和lib目录,所以自己创建 点击OK,选中"Jar Directroy"-->点击"OK" 然后直接把jar复制到这个目录下 ...

  9. Java 集合系列 11 hashmap 和 hashtable 的区别

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  10. 下载模板,上传EXCEL

    说道SAP里对EXCEL操作的大概就是上传,下载,显示了... 下载:(文档是通过SMW0上传的)注:如果下载的时候需要填充EXCEL的值,...请参考另一篇文档,OLE CALL METHOD CL ...